bzoj-1188 分裂游戏

来源:互联网 发布:淘宝天猫积分怎么用 编辑:程序博客网 时间:2024/06/05 03:41

题意:

有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 50using 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;}


0 0
原创粉丝点击