题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3237
解题思路:由于书的高度介于25和32之间,可以把书的高度减去25变成介于0和7之间,这个数字就很适合状态压缩了。思考的时候就尽量往高度进行压缩。每本书有两种选择,一种留下,一种抽走,留下的书和抽走的书可以用两个状态表示,如果抽出去的书不在留下的书里面,那么最后就要增加一段。每本书似乎只和留下来的最后一本书的状态有关,如果书的高度和留下的最后一本书的高度相同,那么可以直接合并进去而不必计算高度,如果不相同则要增加段数。
那我们要怎么表示抽走的状态呢?压缩成一个数字吗?不行,还必须和k扯上关系,也就是说必须有一维表示数量,而抽走书的种类可以用总状态减去留下来的状态来表示。这样就可以用dp[i][j][k][s]来进行状态转移,dp[i][j][k][s]表示到第i本书时取走j本书留下来的书状态为k最后一本书高度为s。状态转移见代码。复杂度O(n*k*(1<<8)*8)。
5 2
25 25 32 32 25
5 1
25 26 25 26 25
0 0
#include <stdio.h> #include <string.h> #define MIN 110 #define INF 1047483647 #define min(a,b) (a)<(b)?(a):(b) int n,m,ht[MIN],ans,one[1<<8]; //one[i]表示状态i中1的个数 int state,dp[2][MIN][1<<8][10];//dp[i][j][k][s]表示到第i行取走j本书剩下的状态数为k最后一本书高度是s时最少连续段数 void Initial() { for (int i = 0; i < (1<<8); ++i) { for (int j = 0; j < 8; ++j) if (i & (1<<j)) one[i]++; } } int main() { int i,j,k,s,cas = 0; int pre,cur,mx,tot; Initial(); while (scanf("%d%d",&n,&m) ,n+m) { mx = state = 0; for (i = 1; i <= n; ++i) { scanf("%d",&ht[i]); ht[i] -= 25; if (ht[i] > mx) mx = ht[i]; //统计最大的高度 state |= (1<<ht[i]); } //Initial mx++ ,tot = (1<<mx); for (j = 0; j <= m; ++j) for (k = 0; k < tot; ++k) for (s = 0; s <= mx; ++s) dp[1][j][k][s] = INF; dp[1][0][1<<ht[1]][ht[1]] = 1; //如果第一本书留下来段数就为1 dp[1][1][0][mx] = 0; //mx其实是不存在的高度,因为第一本书被拿走了那么留下的最后一本书高度实际是不存在的,用这个只是好转移 for (i = 2; i <= n; ++i) { cur = i & 1; //当前状态 pre = 1 - cur; //前一个状态 for (j = 0; j <= m ; ++j) for (k = 0; k < tot; ++k) for (s = 0; s <= mx; ++s) dp[cur][j][k][s] = INF; for (j = 0; j <= m && j < i; ++j) for (k = 0; k < tot; ++k) for (s = 0; s <= mx; ++s) { if (dp[pre][j][k][s] == INF) continue; //小剪枝,但不剪就TLE int stk = k | (1<<ht[i]); //当前的状态 if (j < m) //如果还可以取走 dp[cur][j + 1][k][s] = min(dp[cur][j + 1][k][s], dp[pre][j][k][s]); if (s == ht[i]) //如果和留下来的最后一本高度相同,则当前的书留下来并不增加段数 dp[cur][j][k][s] = min(dp[cur][j][k][s],dp[pre][j][k][s]); else //增加一段 dp[cur][j][stk][ht[i]] = min(dp[cur][j][stk][ht[i]],dp[pre][j][k][s]+1); } } cur = n & 1,ans = n; for (j = 0; j <= m; ++j) for (k = 0; k < tot; ++k) for (s = 0; s < mx; ++s) if (dp[cur][j][k][s] != INF) { int st = state ^ k; ans = min(ans,one[st]+dp[cur][j][k][s]); } printf("Case %d: %d\n\n",++cas,ans); } }
本文ZeroClock原创,但可以转载,因为我们是兄弟。