测试地址:Gems Fight!
题目大意:有
做法:本题需要用到状态压缩DP+博弈+记忆化搜索。
这是一道好题啊…通过分析,发现一个状态仅受后面的状态影响,而且
设
如果能,那么下一轮还是先手取,所以这种情况的状态转移方程为
如果不能,那么下一轮变为后手取,那么这种情况的状态转移方程就是:
注意
下列代码是用记忆化搜索的写法写的,看着比较清楚易懂。
以下是本人代码:
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define inf 1000000000
using namespace std;
int g,b,s,n;
int c[25][11],f[3000010];
bool vis[3000010]={0};
int dp(int x)
{
if (vis[x]) return f[x];
if (x==(1<<b)-1) return 0;
int v=x,i=1,cl[10]={0},add=0;
f[x]=-inf;
while(v)
{
if (v&1)
{
for(int j=1;j<=g;j++)
{
cl[j]+=c[i][j];
cl[j]%=s;
}
}
v>>=1,i++;
}
for(i=1;i<=b;i++)
if (!(x&(1<<(i-1))))
{
add=0;
for(int j=1;j<=g;j++)
add+=(cl[j]+c[i][j])/s;
if (add>0) f[x]=max(f[x],dp(x+(1<<(i-1)))+add);
else f[x]=max(f[x],-dp(x+(1<<(i-1))));
}
vis[x]=1;
return f[x];
}
int main()
{
while(scanf("%d%d%d",&g,&b,&s)&&g&&b&&s)
{
memset(c,0,sizeof(c));
memset(vis,0,sizeof(vis));
for(int i=1;i<=b;i++)
{
scanf("%d",&n);
while(n--)
{
int a;
scanf("%d",&a);
c[i][a]++;
}
}
printf("%d
",dp(0));
}
return 0;
}