Typical DP Contest R - グラフ

問題

N頂点からなる有向グラフが与えられる。
最初全ての頂点は白で、グラフにパス(同じ頂点を何度通ってもよい)を描くと、
パス上の頂点の色が全て黒になる。
この操作を2回できるとき、黒にできる頂点の個数の最大値を求めよ。

制約条件

N≦300

方針

まずは強連結成分分解してDAGに。
トポロジカルソートをして、DAGの根元のほうからDPする。


2本のパスの先端の部分を持つようなDP.
それぞれi, jとすると、iを伸ばすかjを伸ばすか。
以下iを伸ばす場合を考える。


iを伸ばした先kが、jの先祖である場合、jがkを既に通っている可能性がある。
したがって、そのような遷移を不可能にしてやる。


iを伸ばした先kがjの先祖でなければ、普通にパスを伸ばしてよい。
iを伸ばした先kが、jの先祖である場合は、iがjを飛び越すまでスキップして、jの先にkを出してやるようにすれば、全ての場合が尽くされる。


トポロジカル順にDPするのがちゃんとできてなくてちょっとはまった。

ソースコード

int n, sz[300];
bool e[300][300], g[300][300];
vi ord;
 
int dp[300][300];
 
void pre(){
	int m;
	bool ee[300][300];
	cin >> m;
	
	Graph gg(m);
	rep(i, m) rep(j, m){
		cin >> ee[i][j];
		if(ee[i][j]) gg[i].pb(Edge(i, j, 0));
	}
	
	int to[300];
	vector<vi> scc;
	stronglyConnectedComponents(gg, scc);
	
	rep(i, scc.size()){
		rep(j, scc[i].size()) to[scc[i][j]] = i;
		sz[i] = scc[i].size();
	}
	
	n = scc.size();
	Graph g2(n);
	
	rep(i, m) rep(j, m) if(ee[i][j]){
		
		int a = to[i], b = to[j];
		if(a != b && !e[a][b]){
			g[a][b] = e[a][b] = 1;
			g2[a].pb(Edge(a, b, 0));
		}
	}
	topologicalSort(g2, ord);
	
	rep(i, n) g[i][i] = 1;
	rep(k, n) rep(i, n) rep(j, n) g[i][j] |= g[i][k] && g[k][j];
}
int main(){
	pre();
	
	rep(i, n) for(int j = i; j < n; j++){
		dp[i][j] = max(dp[i][j], sz[ord[i]] + (i != j ? sz[ord[j]] : 0));
		
		rep(k, n) if(i != k && j != k){
			if(!g[ord[k]][ord[j]] && e[ord[i]][ord[k]] ||
				g[ord[i]][ord[j]] && e[ord[j]][ord[k]]){
					
					int l = min(k, j), r = max(k, j);
					dp[l][r] = max(dp[l][r], dp[i][j] + sz[ord[k]]);
			}
			if(!g[ord[k]][ord[i]] && e[ord[j]][ord[k]] ||
				g[ord[j]][ord[i]] && e[ord[i]][ord[k]]){
					
					int l = min(k, i), r = max(k, i);
					dp[l][r] = max(dp[l][r], dp[i][j] + sz[ord[k]]);
			}
		}
	}
	
	int ans = 0;
	rep(i, n) rep(j, n) ans = max(ans, dp[i][j]);
	cout << ans << endl;
	
	return 0;
}