博弈

更新/--------------------/2019/1/25

反尼姆博弈:

必胜态需要满足的条件:1,当全为1的时候,判断1的个数。2,否则将全部的堆数异或起来。

题目链接:https://cn.vjudge.net/contest/281037#problem/B

AC代码:

 1 #include<iostream>
 2 #include<stack>
 3 #include<iomanip>
 4 #include<cmath>
 5 #include<stdio.h>
 6 #include<cstring>
 7 #include<algorithm>
 8 #include<queue>
 9 #include<vector>
10 using namespace std;
11 # define ll long long
12 const int maxn =1e3+100;
13 int main()
14 {
15     int T;
16     scanf("%d",&T);
17     while(T--)
18     {
19         int n;
20         scanf("%d",&n);
21         int ans=0,tmp;
22         int num=0;
23         for(int i=1; i<=n; i++)
24         {
25             scanf("%d",&tmp);
26             if(tmp==1)
27                 num++;
28             ans^=tmp;
29         }
30         if(ans&&(num!=n))
31             printf("John\n");
32         else if(ans==0&&num==n)
33             printf("John\n");
34         else
35             printf("Brother\n");
36     }
37     return 0;
38 }
View Code

 

bash博弈:

一堆石子,有n个石子,每一次最多取k个,问你谁先获胜。 直接判断 n % (k +1 )是否为0,如果是0的话就是奇异局(只要对面不是傻逼的话就一定会输的局,否则就是对面赢。

题目链接:https://cn.vjudge.net/contest/257365#problem/A

AC代码:

 1 #include<iostream>
 2 #include<string>
 3 #include<iomanip>
 4 #include<cstring>
 5 #include<cmath>
 6 #include<map>
 7 #include<algorithm>
 8 #include<stdio.h>
 9 using namespace std;
10 # define maxn 1000000+10
11 # define inf 0x3f3f3f3f
12 # define ll long long
13 int main(){
14 int T;
15 scanf("%d",&T);
16 while(T--){
17 int n,m;
18 scanf("%d%d",&n,&m);
19 if(m>=n)printf("A\n");
20 else {
21 if(n%(m+1))printf("A\n");
22 else printf("B\n");
23 }
24 }
25 return 0;
26 
27 }

 

尼姆博弈:

有n堆石子,每一次可以拿任意多个,问你谁拿最后一个。

解题方法:直接将每一堆的石子异或,如果最终结果是0的话就代表是奇异局,否则就是非奇异局。

题目链接:https://cn.vjudge.net/contest/257365#problem/C

AC代码:

 1 #include<iostream>
 2 #include<string>
 3 #include<iomanip>
 4 #include<cstring>
 5 #include<cmath>
 6 #include<map>
 7 #include<algorithm>
 8 #include<stdio.h>
 9 using namespace std;
10 # define maxn 1000000+10
11 # define inf 0x3f3f3f3f
12 # define ll long long
13 int vis[maxn];
14 int ans[maxn];
15 int main()
16 {
17 int n;
18 scanf("%d",&n);
19 int temp;
20 scanf("%d",&temp);
21 for(int i=2;i<=n;i++){
22 int t;
23 scanf("%d",&t);
24 temp=temp^t;
25 }
26 if(temp==0)printf("B\n");
27 else printf("A\n");
28     return 0;
29 }

 

威佐夫博弈:

两堆石子,每一个可以从一堆里面拿任意个或者从两堆里面拿相同的个数,问你最后谁拿完。

判断方法:如果满足 最大值和最小值的差值乘以黄金分割率等于最小值,那么当前的局面就是奇异局,否则就是非奇异局。

题目链接:https://cn.vjudge.net/contest/257365#problem/D

AC代码:

 1 #include<iostream>
 2 #include<string>
 3 #include<iomanip>
 4 #include<cstring>
 5 #include<cmath>
 6 #include<map>
 7 #include<algorithm>
 8 #include<stdio.h>
 9 using namespace std;
10 # define maxn 1000000+10
11 # define inf 0x3f3f3f3f
12 # define ll long long
13 # define gold (sqrt(5.0)+1.0)/2.0
14 ll maxx(ll t1,ll t2){
15 if(t1>t2)return t1;
16 return t2;
17 }
18 ll minn(ll t1,ll t2){
19 if(t1>t2)return t2;
20 return t1;
21 }
22 int main()
23 {
24     ll T;        ll n,m;
25     while(~scanf("%lld%lld",&n,&m))
26     {
27 
28 
29         ll a,b;
30         a=maxx(n,m);
31         b=minn(n,m);
32         int t=(a-b)*gold;
33        // cout<<a<<" "<<b<<" "<<t<<" "<<gold<<endl;
34         if(b==t)printf("0\n");
35         else printf("1\n");
36     }
37     return 0;
38 }

 

分割线/-----------------------------------------------------------------------------------------------------------------------------------/

上面这些只是基本的模型,如果是稍微修改题意就可能套不上模板了,比如说下面这个题。

https://cn.vjudge.net/contest/257365#problem/B

这个时候就需要用到sg函数了,先介绍mex函数,这个函数的作用是先找到第一个在当前的区间内非负的值。

比如说,mex[1 3 5  7] = 0 . mex[ 1 ] = 0 ,mex[2 5 7 8]= 0.mex[ 0] =1;

然后再开始介绍sg,就拿例题作解释。

当只有一颗石子的时候,它的后继状态只有0 (1-1=0) ,sg[1]=mex(sg[0])=1.

当只有两颗石子的时候,他的后继状态变成了1(2-1=1),sg[2]=mex(sg[1])=0,那么这个时候就是奇异局了,也就是说只要对面不是傻逼的话,自己就一定会输。

当只有三颗石子的时候,他的后继状态变成了2(3-1=2),0(3-3=0),所以说,sg[3]=mex(sg[2],sg[0])=2.

以此类推,就可以推出当石子数为n的时候是不是博弈局了。一般的话如果数据量不大的话,可以直接暴力,如果数据量大的话,就可以通过sg打表找规律了。

posted @ 2018-10-15 20:35  Let_Life_Stop  阅读(147)  评论(0编辑  收藏  举报