kmjp's blog

競技プログラミング参加記です

TopCoder SRM 590 Div2 Medium FoxAndGo

Div2 Mediumだけど、Div1とは異なる問題なのでチャレンジ。
http://community.topcoder.com/stat?c=problem_statement&pm=12743

問題

碁盤の状態が与えられる。
ここに黒石を1つ置き、白石を取り除く。取り除ける最大の白石数を答えよ。

なお、隣接マス同士で連結された白石グループが空白マスに接していない場合、その白石グループは取り除かれる。

解法

まず、隣接マス同士で連結された白石をDFSでグループにしていく。
次に、各白石グループに接した空白マス数をカウントする。

この状態で各空白マスに黒石を置いた場合に、取り除かれる白石数をカウントし、最大値を求める。
取り除かれる白石グループは以下の通り。

  • 1つも空白マスに接しない場合
  • 1つだけ空白マスに接しており、その空白マスが黒石を置いた場所の場合

同じグループを重複カウントしないように注意。
ここではsetを使って重複カウントを防いでいる。

int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};

class FoxAndGo {
	public:
	vector<string> V;
	int W,H;
	int ID[51][51];
	int num[2501];
	set<int> E[2501];
	
	void dfs(int y,int x,int id) {
		int i,tx,ty;
		
		ID[y][x]=id;
		num[id]++;
		FOR(i,4) {
			tx=x+dx[i];
			ty=y+dy[i];
			if(tx<0 || tx>=W || ty<0 || ty>=H) continue;
			if(V[ty][tx]=='o' && ID[ty][tx]==-1) dfs(ty,tx,id);
		}
	}
	
	int maxKill(vector <string> board) {
		int i,j,x,y,tx,ty,ret=0;
		V=board;
		H=V.size();
		W=V[0].size();
		
		MINUS(ID);
		ZERO(num);
		int id=0;
		FOR(y,H) FOR(x,W) if(board[y][x]=='o' && ID[y][x]==-1) {
			E[id].clear();
			dfs(y,x,id++);
		}
		
		FOR(y,H) FOR(x,W) if(board[y][x]=='.') {
			FOR(i,4) {
				tx=x+dx[i];
				ty=y+dy[i];
				if(tx<0 || tx>=W || ty<0 || ty>=H) continue;
				if(V[ty][tx]=='o') E[ID[ty][tx]].insert(y*100+x);
			}
		}
		
		FOR(y,H) FOR(x,W) if(board[y][x]=='.') {
			int i,tx,ty;
			set<int> IDs;
			FOR(i,4) {
				tx=x+dx[i];
				ty=y+dy[i];
				if(tx<0 || tx>=W || ty<0 || ty>=H) continue;
				if(V[ty][tx]=='o') IDs.insert(ID[ty][tx]);
			}
			j=0;
			ITR(it,IDs) if(E[*it].size()==1) j+=num[*it];
			ret=max(j,ret);
		}
		FOR(i,id) if(E[i].empty()) ret += num[i];
		return ret;
	}
};

まとめ

自分の書き方が悪いだけかもしれないけど、Div2 Mediumの割に長いコードになってしまった。