UVa12337 Bob’s Beautiful Balls

問題
入力のの順番で、グリッドを渦状に埋め尽くすようにボールを埋める。ただし同じ列は同じ色である必要がある。
グリッドのサイズを N*M としたとき、最大の N+M を求めよ。条件を満たす N,M が存在しない場合は -1 を出力せよ。

解法
ぐるぐる巻きを実装するだけ。次に曲がる位置までの長さを計算するより、「枠外と衝突」or「既に置いたところと衝突」のタイミングで曲がるよう実装する方がミスなく書ける。

以下のコードでは 二重ループと memset で O(N^2 * M^2) になっているように見えるが、N*M = SIZE を満たす組が限定されるので間に合う。

#include <bits/stdc++.h>

using namespace std;

int SIZE;
int ans;
int t[100][100];
string str;

#define REP(i,a,b) for(int i=a;i<(int)b;i++)
#define rep(i,n) REP(i,0,n)

bool invalid(int x, int y) {
  if(y-1 >= 0 && (t[y][x]|t[y-1][x]) != t[y][x]) return true;
  if(y+1 >= 0 && (t[y][x]|t[y+1][x]) != t[y][x]) return true;
  return false;
}

int solve() {
  
  int const dx[] = {1,0,-1,0};
  int const dy[] = {0,1,0,-1};
  int const INF = 999999999;
  
  ans = INF;
  REP(N,2,SIZE) {
    REP(M,2,SIZE) {
      if(N*M != SIZE) continue;
      int px = -1, py = 0, k = 0;
      int idx = 0;
      bool ok = true;
      int touch = 0;
      memset(t, 0, sizeof t);
      for(;;) {
	if(touch > 1) break;
	int nx = px+dx[k], ny = py+dy[k];
	if(nx >= M || ny >= N || nx < 0 || ny < 0) { (k+=1)%=4; continue; }
	if(t[ny][nx] != 0) { (k+=1)%=4; touch ++; continue; }

	touch = 0;
	px = nx, py = ny;
	if(str[idx] == 'B') { t[py][px] = 1; }
	if(str[idx] == 'G') { t[py][px] = 2; }
	if(str[idx] == 'W') { t[py][px] = 4; }
	
	idx++;
	
	if(invalid(px, py)) { ok = false; break; }
      }
      
      if(ok) { ans = min(ans, N+M); }
    }
  }
  
  return (ans == INF) ? -1 : ans;
}

int main() {
  
  int Tc; cin >> Tc;
  REP(Tcnt,1,Tc+1) {
    cin >> str;
    SIZE = str.size();
    cout << "Case " << Tcnt << ": " << solve() << endl;
  }
  
  return 0;
}