http://202.113.2.5:57778/toj/showp1017.html
DP (状态压缩+记忆化搜索) + 博弈论
基本思路:
用状态压缩表示 那些是还可以选的 哪些是不可以选的
在更新过程中 标记 是否是奇异状态
代码及其注释:
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<vector> #include<queue> #include<map> #include<stack> #include<algorithm> using namespace std; //#pragma comment(linker,"/STACK:1000000000,1000000000") #define LL long long const int INF=0x3f3f3f3f; const int M=20; const int N=(1<<M)-1; int fail[N+10];//0 为初始化 1 表示奇异状态 -1 表示非奇异状态 bool can[25];//标记 int Fnext(int x,int l)//在x状态下 选择数字 l+1 (既用位运算表示左移 l 位) 时得到新的状态 { int temp=x; temp=temp|(1<<l); for(int i=1;i<M;++i) { if((temp&(1<<i))&&(i+l+1)<20) { temp=temp|(1<<(i+l+1)); } } return temp; } int dp(int x)//求是否是奇异状态 { if(fail[x]!=0) return fail[x]; if(x==N) { fail[x]=1; return fail[x]; } for(int l=1;l<M;++l) { if((x&(1<<l))) continue; int temp=Fnext(x,l); if(dp(temp)==1)//有任一个可能更新到奇异状态 则当前状态为非奇异状态 fail[x]=-1; } if(fail[x]==0)//否则为奇异状态 fail[x]=1; return fail[x]; } int main() { //freopen("data.txt","r",stdin); memset(fail,0,sizeof(fail)); int T; scanf("%d",&T); for(int ca=1;ca<=T;++ca) { int n; scanf("%d",&n); memset(can,false,sizeof(can)); while(n--) { int temp; scanf("%d",&temp); if(temp<=20&&temp>1) can[--temp]=true; } int k=0; for(int i=1;i<M;++i) { if(can[i]==false) k=Fnext(k,i); } printf("Scenario #%d:\n",ca); if(dp(k)==1)//当前为奇异状态 { printf("There is no winning move"); }else { printf("The winning moves are:"); for(int l=1;l<M;++l) { if((k&(1<<l))) continue; int temp=Fnext(k,l);//非奇异状态的话 看有哪几个选择 if(dp(temp)==1) printf(" %d",l+1); } } printf(".\n\n"); } return 0; }