BZOJ3895 rock
题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3895
看这题感觉好神。
SG函数,dp。。。。好像都不行呀。
最后去膜拜题解发现记忆化搜索 囧
那我就直接上他的做法了。
假设每堆石子的数量都大于1
那么我们定义操作数b为当前石子总数+当前堆数-1
若b为奇数,则先手必胜,否则后手必胜
证明:
若当前只有一堆,则正确性显然
否则:
若b为奇数,那么先手只需进行一次合成操作,此时操作数会-1,且仍不存在大小为1的堆,因此只需要证明b为偶数时先手必败即可
若先手选择了合成操作,那么操作数-1且不存在大小为1的堆,状态回到了b为奇数的状态
若先手取走了某个大小>=3的堆中的一个石子,那么操作数-1且不存在大小为1的堆,状态回到了b为奇数的状态
若先手取走了某个大小为2的堆中的一个石子,那么后手只需要将另一个石子与其它堆合成,b的奇偶性不变且仍不存在大小为1的堆
故b为偶数时先手必败
现在回到一般情况 可能存在大小为1的堆
我们设有a个大小为1的堆,其余堆的操作数为b
那么当前的状态就可以用一个二元组(a,b)来表示
然后就好说了,直接进行记忆化搜索,分情况讨论。
#include <cstdio> #include <cstring> #include <algorithm> #define N 1010 using namespace std; int f[60][50500]; int n; int dfs(int num1,int steps){ if(!num1) return steps&1; if(steps==1) return dfs(num1+1,0); if(~f[num1][steps]) return f[num1][steps]; int &ans=f[num1][steps]; if(num1){ if(!dfs(num1-1,steps)) return ans=1;//取走1的一堆 if(steps && !dfs(num1-1,steps+1)) return ans=1; //将一个1加入多堆中 } if(num1>1){ if(steps && !dfs(num1-2,steps+3)) return ans=1; //合并两堆1 if(!steps && !dfs(num1-2,steps+2)) return ans=1; } if(steps && !dfs(num1,steps-1)) return ans=1; //普通的合并与取石子 return ans=0; } int main(){ memset(f,-1,sizeof(f)); scanf("%d",&n); while(scanf("%d",&n)==1){ int a=0,b=-1; for(int i=1,x;i<=n;i++){ scanf("%d",&x); if(x==1) a++; else b+=x+1; } puts(dfs(a,b)? "YES":"NO"); } return 0; }