题目:
http://acm.hdu.edu.cn/showproblem.php?pid=4778题意:
有b个盒子,每个盒子中有一些石头,石头颜色共有g种颜色,二人轮流把其中一个盒子的石头放入锅中,每s个相同颜色的石头可以生成一种魔法石。
每个人放入一盒石头后的得分为当前生成的魔法石数,如果生成了魔法石可以再放一盒石头。求第一个人得分减去第二个人分数的最大值。
思路:
首先发现这道题的时间是10s,颜色盒子也最多只有21个,所以可以考虑直接状压dp。
主要就是考虑状态转移方程,因为可以连续取盒子,所以方程要分成能生成魔法石和不能生成魔法石两种。
二人的取法都是最优的,所以取得每个状态的分数一定有最大差值。
令dp[i]为第一个人取到 i 状态时第一人得分减去第二人得分的最大值。
如果 j 状态取到 i 状态能够生成魔法石,dp[i] = max(dp[i], res+dp[j]);
如果 j 状态取到 i 状态不能生成魔法石,则相当于 j 状态是第二个人取到的,dp[i] = max(dp[i], res-dp[j]);
j 状态时取第k个盒子得到i状态相当与 j = i^(1<<k);
代码:
#define INF 0x3f3f3f3f
#define N 22
int n,m,g,b,s;
int dp[1<<N],now[N],init[N][N],sum[N];
int main()
{
int i,j,k,kk,t,x,y,z;
while(scanf("%d%d%d",&g,&b,&s)!=EOF&&g)
{
memset(init,0,sizeof(init));
for(i=0;i<b;i++)
{
scanf("%d",&n);
for(j=0;j<n;j++)
{
scanf("%d",&m);
init[i][m]++;
}
}
dp[0] = 0;
for(i=1;i<1<<b;i++)
{
dp[i]=-INF;
memset(sum,0,sizeof(sum));
for(j=0;j<b;j++)
if(!(i&(1<<j)))
for(k=1;k<=g;k++)
sum[k]+=init[j][k];
for(j=1;j<=g;j++)
sum[j]%=s;
for(j=0;j<b;j++)
if(i&(1<<j))
{
memcpy(now,sum,sizeof(sum));
int res=0;
for(k=1;k<=g;k++)
{
now[k]+=init[j][k];
res+=now[k]/s;
}
if(res>0)dp[i] = max(dp[i],res+dp[i^(1<<j)]);
else dp[i] = max(dp[i],res-dp[i^(1<<j)]);
}
}
printf("%d\n",dp[(1<<b)-1]);
}
return 0;
}
本文通过状态压缩动态规划解决了一个涉及石头颜色、数量与魔法石生成的博弈问题。详细阐述了问题背景、解题思路及核心算法,旨在提供一种高效解决此类问题的方法。
2673

被折叠的 条评论
为什么被折叠?



