3c++
3spa
1 1 2code
2blog
3 4ip
3input
2 3 5it
YESio
NOclass
NOim
我天,真神仙题!
这题实际上是博弈论DP,一开始还想着直接用SG * 过去。
咱们先从最简单的入手:
只有一堆石子时咱们能够不考虑合并形成的影响那么一我的赢的状况只有多是他剩下能够进行的操做数是奇数。
(这里咱们发现剩下能够进行的操做数只有取一个石子)
那若是有两个堆。
(假设只有一个石子的堆叫作寂寞堆,大于一个石子的堆叫作热闹堆)
那么咱们要分两种状况分别讨论:
\(1.\) 咱们有两个堆,一个寂寞堆一个热闹堆。
那么咱们假设寂寞堆 \(1\) 个石子,热闹堆 \(2\) 个石子,那么很明显咱们当前只有要么从两个堆里取一个,要么合并。
首先考虑合并:合并以后热闹堆的奇偶性变了,同时合并以后取的是对手,这样就保证了对手赢。
考虑先把寂寞堆取完,那么咱们在热闹堆中是能够直接根据奇偶求出谁会赢。若是先取热闹堆对手是有赢的策略的。
到这里咱们发现好像寂寞堆会影响答案,若是只有热闹堆,热闹堆之间的合并不会改变他们的奇偶,对咱们考虑没有影响,只会致使赢输的人不同。
但若是出现了寂寞堆,寂寞堆的合并会影响热闹堆的奇偶性,因此要特殊考虑寂寞堆。
这个时候就要咱们上博弈论DP了 然而我不知道为何要上(逃
设状态 \(f[i][j]\) 表示有 \(i\) 个寂寞堆,\(j\) 次对于热闹堆的操做时当前操做的人是赢仍是输。
这个状态好诡异
咱们转移怎么办呢?
分类讨论一下:
\(1.\) 寂寞堆操做
\(2.\) 热闹堆操做
这样以后好像就没啥子了。注意一下细节就莫得了。
#include<bits/stdc++.h> using namespace std; int T,n; const int N=55,M=1005; int a[N],f[N][M*N]; inline int dfs(int num,int sum){ if(num<=0 && sum<=0) return 0; if(f[num][sum]!=-1) return f[num][sum]; if(num<=0) return f[num][sum]=(sum&1); if(sum==1) return f[num][sum]=dfs(num+1,0); f[num][sum]=0; if(num && !dfs(num-1,sum)) return f[num][sum]=1; // 拿一个寂寞堆的石子 if(sum && !dfs(num,sum-1)) return f[num][sum]=1; // 把一个热闹堆里拿掉一个石子 if(num && sum && !dfs(num-1,sum+1)) return f[num][sum]=1; // 把一个寂寞堆合并到热闹堆上 if(num>1 && !dfs(num-2,sum+2+(sum?1:0))) return f[num][sum]=1; // 把两个寂寞堆合并 return f[num][sum]; } int main(){ scanf("%d",&T); memset(f,-1,sizeof(f)); while(T--){ int cnt=0,step=0; scanf("%d",&n); for(int i=1;i<=n;++i){ scanf("%d",&a[i]); if(a[i]==1) cnt++; if(a[i]>1) step+=a[i]+1; } if(step) step--; printf("%s\n",dfs(cnt,step)?"YES":"NO"); } return 0; }