题意:
有n堆石头,编号为1-n;
每次操作为选择三个堆,i<j<=k;
取走i中的一个石子并在j,k两堆都放入一个;
不能操作的人输,求先手能否必胜;
若能则输出第一步的字典序最小方案和方案数;
题解:
博弈论的题目一般就是组合游戏加SG函数;
但是稍微一考虑却发现各个石头堆不是独立的,不能直接上组合游戏;
所以这题是HNOI的题,考虑每个石头作为一个游戏;
会发现这样就是独立的游戏了!
那么定义SG函数f[x]表示离第n堆还有x堆的一颗石头的SG值;
显然f[0]=0,枚举比x小的i,j就是x的后继状态,打出了SG函数表;
然后对每个堆中的石头求异或和,求和不为0则先手必胜;
输出方案啥的暴力枚举O(n^3)可以解决;
代码:
#include<stdio.h> #include<string.h> #include<algorithm> #define N 50 using namespace std; int f[N],a[N]; bool hash[N]; int sg(int x) { memset(hash,0,sizeof(hash)); for(int i=0;i<x;i++) for(int j=0;j<=i;j++) hash[f[i]^f[j]]=1; for(int i=0;;i++) { if(!hash[i]) { f[x]=i; break; } } } void init() { for(int i=1;i<=21;i++) sg(i); } int main() { int c,T,n,m,i,j,k,l,ans,cnt; bool flag; init(); scanf("%d",&T); for(c=1;c<=T;c++) { scanf("%d",&n); for(i=1;i<=n;i++) scanf("%d",a+i); for(i=1,ans=0;i<=n;i++) if(a[i]&1) ans^=sg(n-i); if(ans==0) { printf("-1 -1 -1\n0\n"); continue; } for(i=1,flag=0,cnt=0;i<=n;i++) { for(j=i+1;j<=n;j++) { for(k=j;k<=n;k++) { if((ans^f[n-i]^f[n-j]^f[n-k])==0) { if(!flag) printf("%d %d %d\n",i-1,j-1,k-1),flag=1; cnt++; } } } } printf("%d\n",cnt); } return 0; }
原文地址:http://blog.csdn.net/ww140142/article/details/47273213