hdu 4778 Gems Fight! 博弈+状态dp+搜索

时间:2024-08-26 11:04:32

作者:jostree 转载请注明出处 http://www.cnblogs.com/jostree/p/4102743.html

题目链接:hdu 4778 Gems Fight! 博弈+状态dp+搜索

不难发现,无论Alice和Bob以何种方式投掷包裹,他们得到的Magic Stones个数的和$Sum$是一定的,因此只需要计算Alice可以获得的最大的Magic Stones的个数MAXA,则Bob获得的个数为Sum-MAXA,而两者的最大的差为MAXA-(sum-MAXA)为答案。那么如何计算MAXA呢?我们定义状态state为目前包裹的使用情况,如果包裹i被使用则state的二进制的第i位为0,否则为1。我们可以发现如下性质:

Alice和Bob的目的是相同的,即使得获得的Magic Stones的个数最大。唯一的区别就是Alice先手,而Bob后手。如果Alice这一轮没有获得Magic Stone,那么权利转移给Bob,Bob的策略则和Alice在相同的状态state下先手的策略是相同的。从而可以定义dp[state],表示在先手的情况下(无论是Alice还是Bob)能够获得的最大的Magic Stones数目。从而可以使用深度优先的记忆化搜索。对于一个状态$state$,如果第i个包裹还没有被使用,则计算先手把这个包裹投入cooker中所能获得的magic stone的个数gen,如果gen>0则说明可以继续保持先手,获得的Magic Stones得个数等于此次获得和个数加上在下一个状态下先手投掷所能获得的最大的个数。否则的话先手转化为后手,所获得的Magic Stones的个数只能为此状态下所能产生所有的的Magic stones的个数减去先手在下一状态下所能获得的最大的个数。我们用gen表示此次向cooker中投掷i包裹能够获得的magic的个数,cc表示cooker的状态,即各个颜色石头的个数。left表示目前状态下所能产生的所有的Magic stones的个数。

代码如下:

 #include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
using namespace std;
const int MAXB = ;
const int MAXC = ;
const int MIN = -;
int g, b, s;
int bag[MAXB][MAXC];
int color[MAXC];
int dp[<<MAXB];
int dfs(int state,int left, int *c)
{
if( state == || left == ) return ;
if( dp[state] != MIN ) return dp[state];
int maxres = MIN;
int maxtmp = ;
for( int i = ; i < b ; i++ )
{
int cc[MAXC];
memset(cc, , sizeof(cc));
int gen = ;
if( state>>i & )
{
for( int j = ; j < g ; j++ )
{
cc[j] = c[j] + bag[i][j];
gen += cc[j]/s;
cc[j] %= s;
}
if( gen > )
{
maxtmp = gen + dfs(state^(<<i), left-gen, cc);
}
else
{
maxtmp = left - dfs(state^(<<i), left, cc);
}
}
maxres = max(maxres, maxtmp);
}
dp[state] = maxres;
return maxres;
}
int main(int argc, char *argv[])
{
while( )
{
scanf("%d%d%d", &g, &b, &s);
if( g == && b == && s == ) return ;
memset(bag, , sizeof(bag));
memset(color, , sizeof(color));
int sum = ;
for( int i = ; i < b ; i++ )
{
int tmpn;
scanf("%d", &tmpn);
while( tmpn -- )
{
int c;
scanf("%d", &c);
bag[i][c-]++;
color[c-]++;
}
}
for( int i = ; i < g ; i++ )
{
sum+=color[i]/s;
}
for( int i = ; i < (<<MAXB) ; i++ )
{
dp[i] = MIN;
}
int cc[MAXC];
memset(cc, , sizeof(cc));
int res = dfs((<<MAXB)-, sum, cc);
printf("%d\n", res-(sum-res));
}
}