启示
博弈是一个熟悉而又陌生的词汇,我一直没有去深入思考过关于博弈的本质及其原理。最近ACMer的训练,我选择了专攻博弈方向,看了很多博文,查了很多资料,想了很长时间,终于推理出了一套“王氏”博弈理论。
思考
三种经典博弈
懒得写了。
TIPS: 第二种威佐夫博弈有一个神奇的推理公式
若(a,b)是必败态,满足 a < b && a == (int) (b - 1)*(sqrt(5)+1)/2
那坨常数还等价于
1+黄金分割率
POJ 1067 算法验证题
其它游戏
其实,我们可以把一个游戏所有状态标号,形成一个有向图。从游戏的初始状态作为root,最终的输赢状态作为叶节点,这就神奇地把博弈变成了图论的题目了……先手的肯定想走过奇数条边到达胜利的叶节点,后手也是这么想的,然后在双方都不能满足胜利的条件时,都会退而求其次努力去完成和局的条件。差不过所有的博弈都是这么回事。
思路
Gurndy
这个又有人称作sg值,它记录的是,当前状态所能推出所有的子状态的值中,没出现过的最小自然数。
这个函数非常有用,他就实现了我刚才把博弈变成一张图的过程,并且给每个节点都标上了一定的唯一的sg值。
代码实现
我实现取石子游戏的写法,其它博弈根据题意自己推了,类似于DP,能意会,不可言传。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| const int NX=100; int sg[NX]; int move[NX]; int origin; void grundy(){ sg[0]=0; for(int i=1;i<=origin;i++){ set <int > st; for(int j=0;j<NX;j++){ if(move[j] < i){ st.insert(i-move[j]); } } int cnt=0; while(st.count(cnt) != 0)cnt++; sg[i] = cnt; } }
|
练习
POJ博弈水题
POJ 2311
AC代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| #include <iostream> #include <stdio.h> #include <math.h> #include <algorithm> #include <stack> #include <set> #include <cstring> using namespace std; int sg[205][205]; int grundy(int n,int m){ if(sg[n][m]!=-1)return sg[n][m]; set<int> st; for(int i=2;n-i>=2;i++) st.insert(grundy(n-i,m)^grundy(i,m)); for(int j=2;m-j>=2;j++) st.insert(grundy(n,m-j)^grundy(n,j)); int cnt=0; while(st.count(cnt))cnt++; return sg[n][m]=sg[m][n]=cnt; } void init(){ for(int i=0;i<205;i++) for(int j=0;j<205;j++){ sg[i][j]=-1; } } int main(){ init(); int w,h; while(~scanf("%d%d",&w,&h)){ if(grundy(w,h)!=0)puts("WIN"); else puts("LOSE"); }return 0; }
|
POJ 1704
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| #include <iostream> #include <cstdio> #include <math.h> #include <cstring> #include <algorithm> using namespace std; #define inf 0x3f3f3f3f const int MAXN=100005; int main(){ long long t;cin>>t; while(t--){ bool stan=1; int n,i=1,ans=0;int tt[1005]; cin>>n; for(i=1;i<=n;i++){ scanf("%d",tt+i); } sort(tt+1,tt+n+1); i=1; if(n&1){ i=2; ans=ans^(tt[1]-1); } for(;i+1<=n;i+=2){ ans=ans^(tt[i+1]-tt[i]-1); } if(!ans)puts("Bob will win"); else puts("Georgia will win"); } return 0; }
|
POJ 2348
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include <iostream> #include <cstdio> #include <math.h> #include <cstring> #include <algorithm> using namespace std; #define inf 0x3f3f3f3f const int MAXN=100005; int main(){ long long a,b; while(cin>>a>>b){ if(a==0&&b==0)break; bool stan=1; if(a!=0&&b!=0){ while(1){ if(a<b)swap(a,b); if(a%b==0)break; if(a>2*b)break; stan=!stan; a-=b; } } if(stan)puts("Stan wins"); else puts("Ollie wins"); } return 0; }
|