UVa 12235 - Help Bubu
Published on 2016-10-23描述
有 本放在书架上,高度分别为 ,定义 的混乱值为权值的段数,现在你可以将 本书拿出来,放在书中间的任意一个位置(不改变其余书的顺序),请问最终能得到的最小混乱值是多少?
分析
第一眼看上去本题不好做,但是我们发现这个题的限制其实是很松的,书的段的顺序其实没有关系,只需要知道最后一段是谁就行了。而且这个题权值只有 8 种情况,这启发我们进行状压 DP:设 为在前 本书中拿走 本书,剩下排在最后的一本书是 ,剩下的书的权值状况为 的最小混乱值。
状态转移采用刷表转移,分为拿走 这本书和不拿走 这本书,拿走的书就不管了,不拿走的书要计算答案,详情参见代码。注意 表示现在没有书留下,这时候最后一个是谁是没有意义的,所以要判一下。
上述 DP 中我们对拿走的书没有进行处理,我们把它留到最后,对于最终状态 ,不妨设 为全体权值的集合,那么这个状态最后的答案为 ,原因是对于 中有的权值,那么拿走的书中是这个权值的可以直接插入,不增加混乱值;而对于没有的权值,拿走的书中是这个权值的就必须新开辟一个段,增加 1 的混乱值。
总结:本题 DP 不难想到,重要的是拿走的书不当时处理,而是最后一起处理,这样就可以处理拿走的书放在后面的情况,避免漏掉最优解。
代码
// Created by Sengxian on 2016/10/21. // Copyright (c) 2016年 Sengxian. All rights reserved. // UVa 12235 状压 DP #include <bits/stdc++.h> using namespace std; typedef long long ll; inline int ReadInt() { static int n, ch; n = 0, ch = getchar(); while (!isdigit(ch)) ch = getchar(); while (isdigit(ch)) n = n * 10 + ch - '0', ch = getchar(); return n; } const int MAX_N = 100 + 3, MAX_K = 100 + 3, MAX_V = 8; int n, K, a[MAX_N], dp[2][MAX_V][MAX_K][1 << MAX_V]; inline void tension(int &a, const int &b) { if (b < a) a = b; } int main() { #ifndef ONLINE_JUDGE freopen("test.in", "r", stdin); #endif int caseNum = 0; while (n = ReadInt(), K = ReadInt(), n + K) { int ALL = 0; for (int i = 0; i < n; ++i) a[i] = ReadInt() - 25, ALL |= 1 << a[i]; memset(dp[0], 0x3f, sizeof dp[0]); dp[0][a[0]][0][1 << a[0]] = 1; dp[0][0][1][0] = 0; int cur = 0; for (int i = 0; i < n - 1; ++i, cur ^= 1) { memset(dp[!cur], 0x3f, sizeof dp[!cur]); for (int s = 0; s < (1 << MAX_V); ++s) for (int j = 0; j < MAX_V; ++j) for (int k = 0; k <= K; ++k) if (dp[cur][j][k][s] != 0x3f3f3f3f) { if (j == a[i + 1] && s) tension(dp[!cur][j][k][s], dp[cur][j][k][s]); else { tension(dp[!cur][j][k + 1][s], dp[cur][j][k][s]); tension(dp[!cur][a[i + 1]][k][s | (1 << a[i + 1])], dp[cur][j][k][s] + 1); } } } int ans = INT_MAX; for (int s = 0; s < (1 << MAX_V); ++s) for (int j = 0; j < MAX_V; ++j) for (int k = 0; k <= K; ++k) tension(ans, dp[cur][j][k][s] + __builtin_popcount(ALL ^ s)); printf("Case %d: %d\n\n", ++caseNum, ans); } return 0; }