博弈
更新/--------------------/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 }
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打表找规律了。