TopCoder SRM517 Div1Hard

問題

真っ白な長方形のグリッドに対して次の操作ができる。「ある行か列を選んで、好きな色で全部塗りつぶす」白く塗ることはできない。すでに塗ってあるところの色は変わる。目標の状態にするには最低何回操作する必要があるか?

解法

ある行や列に対して、複数回の操作をする必要がないことは明らか。また、すべての行、列に対して操作をする必要はない(一番最初に塗ったものが無駄になる)。
すると、操作をされない(垂直方向の操作ですべて塗られる)列がある。
それを最初に決めると、垂直方向にどのように塗ったかがわかる。他の列に対して調べると、その列と、各行で、どちらが先に塗られたかがわかる。前後関係をグラフにして、サイクルがないかチェックすればよい。これを、行と列を入れ替えてまた行う。

コード

#include <vector>
#include <string>

using namespace std;

class ColorfulBoard
{
public:
	bool graph[100][100];
	int sz;
	bool visited[100];
	
	bool visit(int p, int q)
	{
		if(p==q&&visited[p]) return true;
		if(visited[p]) return false;
		visited[p] = true;
		for(int i=0;i<sz;i++) if(graph[p][i]&&visit(i, q)) return true;
		return false;
	}
	
	bool check()
	{
		for(int i=0;i<sz;i++){
			for(int j=0;j<sz;j++) visited[j] = false;
			if(visit(i,i)) return false;
		}
		return true;
	}
	
	int test(vector<string> in)
	{
		int W = in.size(), H = in[0].size();
		sz = W+H;
		
		int R = 100000;
		for(int i=0;i<W;i++){
			//in[i] is covered without painting this row
			int ret = H;
			
			for(int j=0;j<H+W;j++){
				for(int k=0;k<H+W;k++) graph[j][k] = false;
			}
			
			for(int j=0;j<W;j++) if(in[i]!=in[j]){
				ret++;
				
				char difC = '.';
				for(int k=0;k<H;k++){
					if(in[j][k] != in[i][k]){
						if(difC=='.' || difC==in[j][k]) difC = in[j][k];
						else difC = '!';
					}
				}
				
				if(difC=='!') goto nex;
				
				for(int k=0;k<H;k++){
					if(in[j][k] == in[i][k]){
						if(in[j][k]!=difC) graph[k+W][j] = true;
					}else{
						graph[j][k+W] = true;
					}
				}
			}
			
			/*for(int j=0;j<sz;j++){
				for(int k=0;k<sz;k++) printf("%d ", graph[j][k]);
				puts("");
			}
			puts("");*/
			if(check()) R = min(R, ret);
nex:
			continue;
		}
		
		return R;
	}
	
	int theMin(vector<string> board)
	{
		int ret = 100000;
		ret = min(ret, test(board));
		
		vector<string> b2;
		for(int i=0;i<board[0].size();i++){
			string s;
			for(int j=0;j<board.size();j++) s.push_back(board[j][i]);
			b2.push_back(s);
		}
		
		//for(int i=0;i<b2.size();i++) printf("%s\n", b2[i].c_str());
		//printf("%d\n", test(b2));
		ret = min(ret, test(b2));
		
		if(ret>1000) return -1;
		return ret;
	}
};