P3185 [HNOI2007] 分裂游戏
P3185 [HNOI2007] 分裂游戏
题目描述
聪聪和睿睿最近迷上了一款叫做分裂的游戏。
该游戏的规则是: 共有
两人最后决定由聪聪先取豆子,为了能够得到最终的巧克力豆,聪聪自然希望赢得比赛。他思考了一下,发现在有的情况下,先拿的人一定有办法取胜,但是他不知道对于其他情况是否有必胜策略,更不知道第一步该如何取。他决定偷偷请教聪明的你,希望你能告诉他,在给定每个瓶子中的最初豆子数后是否能让自己得到所有巧克力豆,他还希望你告诉他第一步该如何取,并且为了必胜,第一步有多少种取法?
输入格式
输入文件第一行是一个整数
每组测试数据的第一行是瓶子的个数
输出格式
对于每组测试数据,输出包括两行,第一行为用一个空格两两隔开的三个整数,表示要想赢得游戏,第一步应该选取的
第二行表示要想确保赢得比赛,第一步有多少种不同的取法。
提示
Solution:
博弈论部分:
P:
MnZn 刚学博弈论 1ms ......
首先我们先来介绍一下博弈论这一坨东西:定义博弈状态:
基本知识:
定理 1:没有后继状态的状态是必败状态。
定理 2:一个状态是必胜状态当且仅当存在至少一个必败状态为它的后继状态。
定理 3:一个状态是必败状态当且仅当它的所有后继状态均为必胜状态。
—— oiwiki
然后我们以 Nim 游戏 为例来说一下 sg 函数:
首先我们要进行一个转化:我们假设两个互不相交状态A,B,只要满足:
- 1:没有后继状态的状态是
。 - 2:一个状态是
当且仅当存在至少一个 为它的后继状态。 - 3:一个状态是
当且仅当它的所有后继状态均为 。
那么就可以说
然后我们定义:
定义一个状态
然后我们将
在 Nim 游戏中,我们将石子个数定为
显然满足 :
- 1:没有后继状态的状态是
。
然后我们只需证明:
- 2 :一个状态是
当且仅当存在至少一个 为它的后继状态。 - 3:一个状态是
当且仅当它的所有后继状态均为 。
对于2,我们设
对于 3:很显然随便改一个数字就变成0了。
本题思路:
我们首先我们发现先手的操作是可以被后手仿照的,所以我们只关心豆子的奇偶性,然后我们
Code:
#include<bits/stdc++.h> const int N=21; using namespace std; int sg[N+5],vis[N<<2],a[N+5]; struct ANS{ int i,j,k,cnt; }ans; int n; void get_sg() { sg[1]=0; for(int i=2;i<=N;i++) { for(int j=1;j<=i;j++) { for(int k=j;k<i;k++) { vis[sg[j]^sg[k]]=i; for(sg[i]=0;vis[sg[i]]==i;sg[i]++); } } } } void solve(int x) { ans={-1,-1,-1,0}; for(int i=n;i;i--)for(int j=i-1;j;j--)for(int k=j;k;k--) { if(!(x^sg[i]^sg[j]^sg[k])) { if(!ans.cnt)ans=(ANS){n-i,n-j,n-k,1};反转 else ans.cnt++; } } printf("%d %d %d\n%d\n",ans.i,ans.j,ans.k,ans.cnt); } void work() { cin>>n; int x=0; for(int i=n;i;i--)cin>>a[i];//注意,这里将数组反转了。 for(int i=1;i<=n;i++)if(a[i]&1)x^=sg[i]; solve(x); } int main() { //freopen("P3185.in","r",stdin);freopen("P3185.out","w",stdout); ios_base::sync_with_stdio(0); cin.tie(0),cout.tie(0); get_sg(); int T; cin>>T; while(T--)work(); return 0; }