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;
}
转载于:https://www.cnblogs.com/liulangye/archive/2012/10/09/2717316.html