题解 UVA10118 【免费糖果 Free Candies】
前言:
这是一道难度并不大的DP...
然鹅题解中某些神犇的讲解让我很懵逼0.0..
于是过来发篇题解...
题目大意:
有四堆糖果,每次可以从四堆糖果中拿某堆顶上的一颗糖果,放到袋子里,如果袋子里有两份相同的糖果,那么就可以把那两枚糖果拿到口袋里...如果袋子里糖果数超过5枚,游戏结束,求最多可以拿多少对糖果
题目分析:
首先,我们分析数据规模:
似乎用DP处理这道题是可行的...
我们注意到:每次拿糖果都只能拿堆顶糖果...
所以说,剩下哪些糖果没拿,可以转化为四堆中分别剩下糖果的数目..
那么如何表示已经拿走的糖果?
不,已经拿走的糖果我们不需要关心,我们关心袋子里的糖果就好了...
如何处理袋子中的糖果?状态压缩?
使用最裸的方法,我们可以开
很显然,空间爆炸的厉害...
我们考虑优化:
设糖果总集为U,剩下的集合为A,拿走的集合为B,A是B的补集,故若A唯一确定,那么B也唯一确定...又因为
而显然,
故我们设状态
转移便是转移到分别从每一堆中拿走一个糖果后的状态...
具体细节见代码...
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 44;
int n;
int d[N][4];
int f[N][N][N][N];//从第一堆拿了k个,第二堆拿了i个,第三堆拿了j个,第四堆拿了c个后
//从剩下的糖果中拿取而获得的最大价值
//处理某一个数中有多少个1,也就是袋子里有多少糖果
int count_1(int x)
{
int num = 0;
while(x)
{
num += ((x & 1) == 0 ? 0 : 1);
x >>= 1;
}
return num;
}
//这里有一个小技巧,dfs传参的时候传数组,可以避免掉手写四种转移
//top[]代表每一个堆各拿了多少个糖,now表示二进制压缩后的袋子中的糖果情况
int dfs(int top[], int now)
{
int k = top[0], i = top[1], j = top[2], c = top[3];
//是否处理过了
if(f[k][i][j][c] != -1)
return f[k][i][j][c];
//如果有5个不一样的糖果,那么就不能装糖果了,把答案设为极小值返回
if(count_1(now) > 4)
return f[k][i][j][c] = 0xcfcfcfcf;
f[k][i][j][c] = 0;
for(int o = 0; o < 4; o++)
if(top[o] != n)
{
int tot = 0;
//能否“消掉”袋子里的糖
if(now & (1 << (d[top[o] + 1][o] - 1)))
tot = 1;
//转移 (直接修改now后传到下一次DFS里可以避免计算)
//这个位运算应该还好理解?
now ^= (1 << (d[1 + top[o]++][o] - 1));
tot += dfs(top, now);
now ^= (1 << (d[--top[o] + 1][o] - 1));
//取最大值
f[k][i][j][c] = max(f[k][i][j][c], tot);
}
return f[k][i][j][c];
}
int main()
{
while(scanf("%d", &n) != EOF && n)
{
//多测不清空,爆零两行泪
memset(f, -1, sizeof(f));
for(int k = 1; k <= n; k++)
for(int i = 0; i < 4; i++)
scanf("%d", &d[k][i]);
int ori[4] = {0, 0, 0, 0};
printf("%d\n", dfs(ori , 0));
}
return 0;
}
结语:
如果本题解有BUG...
那么...那么...那么...
(随意了)还请私信作者....