杭電劉春英老師的課件中有:
必敗點(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 1000010using 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 1000010using 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;}