はまやんはまやんはまやん

hamayanhamayan's blog

Coloring Trees [Codeforces 369 : Div2 C]

問題

http://codeforces.com/contest/711/problem/C

n本の木がある。
これらの木をm種類の色で塗る。
木iは色C[i]で塗られているが、一部(C[i]=0のもの)は色が塗られていない。
塗られていない木に色を塗っていくのだが、木iに色jを塗るときはp[i][j]のインクが必要。
木達の美しさを隣り合う色が同じ木をまとめた成分数と定義する。

2,1,1,1,3,2,2,3,1,3
であれば、隣り合う色が同じ木をまとめると
{2}, {1, 1, 1}, {3}, {2, 2}, {3}, {1}, {3}
となり成分数は7なので、美しさは7

この時塗られていない木を塗って、美しさをkとする時に必要な最小インク総数は?
不可能なときは-1を出力。

1 <= k <= n <= 100
1 <= m <= 100
0 <= c[i] <= m
1 <= p[i][j] <= 10^9

考察

1. 例の1番目が何も塗られてない木が3本あり、美しさを2にする問題
2. 何も塗られてない状況から考えろってこと?
3. ソートは順が関係あるから無理なんで、先頭から色を決定していく感じ?
4. 先頭から色を決定していくと、塗りたい木の1つ前の色だけに対応して美しさが増減する
5. だいぶ状態がまとめられそうだぞ・・・
6. しかも「最小」インク総数を求める
7. これは…DP!

8. DPを使って解きます

dp[i][j][k] = i番目まで塗って、最後の色がjで、今までの美しさがkである時の最小インク総数
初期化 : dp[0][0][0] = 0, dp[他] = INF
dp[i][j][k] -> 色ii(j == ii)を塗る dp[i + 1][ii][k] + P[j][ii]
            -> 色ii(j != ii)を塗る dp[i + 1][ii][k + 1] + P[j][ii]

9. これを元々色が塗ってある場合で場合分けをして拡張する
10. 場合分け先では、8.のDPでは全て色を塗るよう試したのに対し、塗られている色だけで塗る処理をする
11. この時はインクを使わないので、dpには特に何も足さない

12. 最後に答えをまとめて答える

実装

http://codeforces.com/contest/711/submission/20237906

#define INF 1LL<<60
typedef long long ll;
int N, M, K;
int C[101];
int P[101][101];

// dp[i][j][k] = i番目まで塗って、最後の色がjで、今までの美しさがkである時の塗る最小コスト
ll dp[101][101][101];
//-----------------------------------------------------------------
int main() {
	cin >> N >> M >> K;
	rep(i, 0, N) cin >> C[i];
	rep(i, 0, N) rep(j, 0, M) cin >> P[i][j + 1];
	
	rep(i, 0, N + 1) rep(j, 0, M + 1) rep(k, 0, K + 1) dp[i][j][k] = INF;
	dp[0][0][0] = 0;

	rep(i, 0, N) rep(j, 0, M + 1) rep(k, 0, K + 1) if(dp[i][j][k] != INF){
		if (C[i] == 0) {
			rep(ii, 1, M + 1) {
				int kk = k;
				if (j != ii) kk++;
				dp[i + 1][ii][kk] = min(dp[i + 1][ii][kk], dp[i][j][k] + P[i][ii]);
			}
		}
		else
		{
			int kk = k;
			if (j != C[i]) kk++;
			dp[i + 1][C[i]][kk] = min(dp[i + 1][C[i]][kk], dp[i][j][k]);
		}
	}

	ll ans = INF;
	rep(j, 1, M + 1) ans = min(ans, dp[N][j][K]);

	if (ans == INF) ans = -1;
	cout << ans << endl;
}