杭电刘春英老师的课件中有:
必败点(P点) :前一个选手(Previous player)将取胜的位置称为必败点。
必胜点(N点) :下一个选手(Next player)将取胜的位置称为必胜点。
步骤1:将所有终结位置标记为必败点(P点);
步骤2: 将所有一步操作能进入必败点(P点)的位置标记为必胜点(N点)
步骤3:如果从某个点开始的所有一步操作都只能进入必胜点(N点) ,则将该点标记为必败点(P点) ;
步骤4: 如果在步骤3未能找到新的必败(P点),则算法终止;否则,返回到步骤2。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<set>
#define MAXN 1000010
using namespace std;
int main()
{
//freopen("in.txt","r",stdin);
set<int>win,lose;
bool vis[MAXN*2];
int n,m,p[11];
while(cin>>n>>m)
{
memset(vis,false,sizeof(vis));
win.clear();
lose.clear();
lose.insert(0);
vis[0]=true;
for(int i=0; i<m; i++)
{
int a;
cin>>a;
p[i]=a;
win.insert(a);
vis[a]=true;
}
int cur=0;
while(1)
{
if(vis[n])
break;
if(vis[cur])
cur++;
else
{
int flag=0;
for(int i=0; i<m; i++)
{
if(cur>=p[i]&&!win.count(cur-p[i]))
{
flag=1;
break;
}
}
if(!flag)
{
lose.insert(cur);
vis[cur]=true;
for(int i=0; i<m; i++)
{
win.insert(cur+p[i]);
vis[cur+p[i]]=true;
}
}
cur++;
}
}
if(win.count(n))
cout<<"Stan wins\n";
else cout<<"Ollie wins"<<endl;
}
return 0;
}
对于只剩i块石头时,i点要么是必胜点,要么是必败点,如果有一个p[ ],使得i-p[ ]块石头是个必败点,那么i点是个必胜点,否则就是个必败点。下面是AC代码:
#include<cstdio>
#include<iostream>
#include<cstring>
#include<set>
#define MAXN 1000010
using namespace std;
int main()
{
//freopen("in.txt","r",stdin);
bool f[1000010];
int p[11],m,n;
while(cin>>n>>m)
{
memset(f,false,sizeof(f));
for(int i=0; i<m; i++)
{
cin>>p[i];
f[p[i]]=true;
}
for(int i=1; i<=n; i++)
{
if(f[i]==true) continue;
for(int j=0; j<m; j++)
{
if(i>=p[j]&&f[i-p[j]]==false)
{
f[i]=true;
break;
}
}
}
if(f[n]==true)
cout<<"Stan wins"<<endl;
else cout<<"Ollie wins"<<endl;
}
return 0;
}