题目大意:
有g种不同颜色的小球,b个袋子,每个袋子里面有若干个每种小球
两人轮流取袋子,当袋子里面的同色小球有s个时,会合并成一个魔法球,并被此次取袋子的人获得
成功获得魔法球的人可以再次取
求二者都进行最优策略之后两人所得魔法球个数差
分析:
博弈,数据很小,自然想到了可以搜索所有状态
然后从每一步的子状态中找到对当前人(这一步的先手)最有利的状态即可
直接搜索还是会超时的,于是想到用状态压缩一下,做记忆化搜索
然后其实就是一个状压dp了
通过某个状态对于先手的最优子状态进行转移。。
代码如下
#include <iostream> #include <stdio.h> #include<string.h> #include<algorithm> #include<string> #include<ctype.h> using namespace std; #define MAXN 10000 typedef struct Node { int a,b; }node; node dp[2097152]; bool vi[2097152]; int num[25][25]; int p[8]; int g,b,s,k,m,x; void dfs(int state,int turn) { if(state==(1<<b)-1) { dp[state].a=0; dp[state].b=0; return ; } if(vi[state]) { return ; } int ok; node t; node ans; ans.a=-1000000; ans.b=0; int pp[8]; memcpy(pp,p,sizeof(pp)); for(int i=0;i<b;i++) { t.a=0; t.b=0; ok=0; if(state&(1<<i)) continue; int st=state|(1<<i); //状态 for(int j=0;j<g;j++) { p[j]+=num[i][j]; ok+=p[j]/s; p[j]%=s; } int tur=ok?turn:!turn;//是否交换选手 dfs(st,tur); memcpy(p,pp,sizeof(pp)); t.a+=ok; if(tur==turn) //子状态的先手所要转移到的状态相同 { t.a+=dp[st].a; t.b+=dp[st].b; } else //选手交换了 { t.b+=dp[st].a; t.a+=dp[st].b; } if(t.a-t.b>ans.a-ans.b) { ans=t; } } vi[state]=1; dp[state]=ans; return ; } int main() { while(scanf("%d%d%d",&g,&b,&s),g+b+s) { memset(num,0,sizeof(num)); for(int i=0;i<b;i++) { scanf("%d",&k); while(k--) { scanf("%d",&x); num[i][x-1]++; } } memset(p,0,sizeof(p)); memset(vi,0,sizeof(vi)); dfs(0,0); printf("%d ",dp[0].a-dp[0].b); } return 0; }