HDU组合游戏与博弈论入门小结(未完

来源:互联网 发布:手机怎么添加wifi网络 编辑:程序博客网 时间:2024/04/30 02:22

1.组合游戏的定义:

组合游戏是一个常见的概念,它是是满足这样一些条件的游戏:

1)        游戏有2名参与者,每个人都采用对自己最有利的策略。

2)        参与者操作时可以的操作时将游戏从当前状态改变为另一状态。

3)        参与者轮流进行操作,每个玩家可选择的状态是固定的,没有随机成分

4)        在游戏出于某状态,当前参与者不能进行操作时,游戏结束。此时参照规则决定胜负。

5)        无论参与者做出怎样的操作游戏在有限部数之内结束(没有平局),所以组合游戏必然是一个无环图


组合游戏也可以用一个有向图来表示 G=(X,F)X为游戏的状态集合F(X)为X可以到达的状态集合,而结束状态均宣布当前参与者失败。


这类博弈问题还有若干分类。

规定移动最后一步的游戏者胜的规则叫做Normal Play Rule

规定移动最后一步的游戏者输的规则叫做Misere Play Rule 也称Anti-SG games

此外,对于游戏的双方,如果二者博弈的规则相同,那么称这类游戏是impartial games否则称为partizan games 

实际上,解决partizan games的方法和普通的SG games是有区别的,一般会采用动态规划或者surreal number

 

2.必胜局面

组合游戏的必胜状局面定义为当前玩家有策略能使无论对手做什么操作也可以保证自己胜利的状态。

1)        结束状态的性质由规则决定。

2)        一个非结束状态,如果它能到达任何必败状态,那么它是必胜状态,否则它就是必败状态。

 

3.Sprague-Grundy 函数

它是定义在组合游戏状态上的函数,用g (X)表示X状态的函数值。

定义为:

            g (X)= min{n| n∈N ,n≠ for y∈F(x)}

形象的说就是X的函数值为与X所能到达的任意点函数值不同的最小自然数。

我想细心的读者已经发现g (X)=0当且仅当X为必败状态,这用其定义很容易验证:

1)        当X为结束状态时,由于g (X)=0它是必败状态。

2)        当X不是结束状态时,如果它能到达必败状态,那么g (X)>0,X是必胜状态。

3)        如果X不是结束状态且它到达的所有状态未必败状态,那么g (X)=0它是必败状态。


三大基本博弈:

(一)巴什博奕Bash Game):

只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个.最后取光者得胜.

n = (m+1)r+s , (r为任意自然数,s≤m), 即n%(m+1) != 0, 则先取者肯定获胜。

巴什博弈还是很好理解的,以你是先手的角度考虑,每一局你都必须构建一个局势,每次都留给对手m+1的倍数个物品。所以不只是取物品中的博弈可以用到巴什定理,还可以是报数之类的,看谁先报到100.并且每次报的数必须是1~10(包括1跟10),那么你每次都应该留给对手剩下的报数个数为11的倍数。如果n=m+1,那么由于一次最多只能取m个,所以,无论先取者拿走多少个,后取者都能够一次拿走剩余的物品,后者取胜。因此我们发现了如何取胜的法则:如果n=(m+1)r+s,(r为任意自然数,s≤m),那么先取者要拿走s个物品,如果后取者拿走k(≤m)个,那么先取者再拿走m+1-k个,结果剩下(m+1)(r-1)个,以后保持这样的取法,那么先取者肯定获胜。总之,要保持给对手留下(m+1)的倍数,就能最后获胜。

题目:

HDU1846:常规做法

<span style="font-size:14px;">#include<cstdio>int main(){        int m,n,i,c;        while(~scanf("%d",&c))        {                while(c--)                {                        scanf("%d%d",&m,&n);                        bool flag=true;                        if(m%(n+1)==0)                                flag=false;                            if(flag)                                   printf("first\n");                            else                                printf("second\n");                }             }        return 0;  }                </span>

HDU1847:由于可以抽二的倍数 所以只判断2+1=3就好

#include<cstdio>int main(){        int n;        while(~scanf("%d",&n))        {                if(n%3==0)                        printf("Cici\n");                else                        printf("Kiki\n");        }        return 0;}   

HDU2147:把棋盘的左下角看成(1,1)那么起始点就是(n,m),最后谁能得到(1,1)这个点谁就胜,可以看出只要先抢到(奇数,奇数)接下来无论对方怎么走都会至少有一个坐标是偶数,那么对方就一定会败,先者有三种走发(n-1,m),(n,m-1),(n-1,m-1)只有m和n都是奇数的时候第一步走不出来(奇数,奇数)其余的(只要有一个是偶数)就肯定会得到必胜态(奇,奇),就会赢。

#include<iostream>using namespace std;int main(){        int n,m;        while(cin>>n>>m,m+n)        {                puts((n%2&&m%2)?"What a pity!":"Wonderful!");    }        return 0;}

HDU2149:基本做法

#include<cstdio>int main(){        int n,m;        while(~scanf("%d%d",&m,&n))    {                if(m%(n+1)==0)                        printf("none\n");                else if(n>m)                {                        printf("%d",m);                        for(int i=m+1;i<=n;i++)                        {                                printf("% d",i);                        }                        printf("\n");                }                else                        printf("%d\n",m%(n+1));        }        return 0;}

HDU2188:基本做法

#include<cstdio>int main(){       int c,n,m;        while(~scanf("%d",&c))        {                while(c--)                {                        scanf("%d%d",&n,&m);                        if(n%(m+1)==0)                                printf("Rabbit\n");                        else                                printf("Grass\n");                }        }}

HDU2897:巴什博弈的变形 假设先取者为A,后取者为B,初始状态下有石子n个,除最后一次外其他每次取得石子个数必须在[p,q]之间。 
若当前石子共有n =(p+q)* r个,A必胜,策略为A第一次取q个,以后每次若B取K个,A取(p+q-k)个,如此下去最后必剩下p个给B,所以A必胜。 
若n =(p+q)*r+s个(1<s<=p)B胜,策略为每次取石子活动中,若A取k个,则B去(p+q-k)个,最后剩下s个给A,此时s<= p,A一次取完,B胜。 
若n =(p+q)* r+s个(p<s<=q)A胜,策略为A第一次取t(1<s–t<= p)个,以后每次B取k个,则A取(p+q-k)个,那么最后留下1<s-t<=p给B,则A胜。

#include<cstdio>int main(){        int n,p,q;        while(~scanf("%d%d%d",&n,&p,&q))        {                if(n%(q+p)>0&&n%(q+p)<=p)                        printf("LOST\n");                else                        printf("WIN\n");        }        return 0;} 
理解了bash的规则这种题还是很水的


(二)威佐夫博奕Wythoff Game

有两堆各若干个物品,两个人轮流从某一堆或同时从两堆中取同样多的物品,规定每次至少取一个,多者不限,最后取光者得胜。用(a[k],b[k])(a[k] ≤ b[k] ,k=0,1,2,...,n)表示两堆物品的数量并称其为局势,如果甲面对(0,0)甲输,称这种局势为奇异局势。前几个奇异局势是:(0,0),(1,2),(3,5),(4,7),(6,10),(8,13),(9,15),(11,18),(12,20)。

仔细观察规律可以发现可以看出:

1.任何自然数都包且仅包含在一个奇异局势中
ak是未在前面出现过的最小自然数,所以有a[k] > a[k-1] ,而且 b[k] = a[k] + k > a[k-1] + k > a[k-1] + k - 1 = b[k-1] > a[k-1] 。
2.将奇异局势变为非奇异局势
只改变奇异局势(a[k],b[k])的某一个分量,那么另一个分量不可能在其他奇异局势中,所以必然是非奇异局势。如果使(a[k],b[k])的两个分量同时减少,则由于其差不变,也不可能是其他奇异局势的差,因此也是非奇异局势。
3.将非奇异局势变为奇异局势。
假设面对的局势是(a , b):
1).若 b = a:同时从两堆中取走 a 个物体,就变为了奇异局势(0,0)
2).若 a = a[k] ,b > b[k] :那么,取走b - b[k]个物体,即变为奇异局势
3).若 a = a[k] ,b < b[k] :因为a和b唯一确定a-b,则可以变为奇异局势( a[b-a], a[b-a] + b - a),同时从两堆中拿走a - a[b-a] 个物体
4).若a > a[k] ,b= a[k] + k  :从第一堆中拿走多余的数量a - a[k] 
5).若a < a[k] ,b= a[k] + k: 有两种做法。第一种,a=a[j] (j < k)从第二堆里面拿走 b - b[j] 即可;第二种,a=b[j] (j < k)从第二堆里面拿走 b - a[j] 即可。
          由上述性质可知,如果双方都采取正确操作,那么面对非奇异局势,先取者必胜。
判断奇异局势的公式为
                                       a[k] =(int) [k(1+√5)/2]   ,   b[k] = a[k] + k ((1+√5)/2 为黄金分割数
因此,由a[k],b[k]组成的矩形近似为黄金矩形,由于2/(1+√5)=(√5-1)/2,可以先求出 j = [a(√5-1)/2],若 a = [j(1+√5)/2],那么a = a[j],b[j] = a[j] + j,若不等于,那么a = a[j]+1,b = a[j] + j + 1,若都不是,那么就不是奇异局势。然后再按照上述法则进行,一定会遇到奇异局势。
题目:
HDU1527:威佐夫博奕模板题
#include<stdio.h>#include<math.h>int main(){    int n,m,temp,k;    while(~scanf("%d%d",&n,&m))    {        if(n<m)        {            temp=n;            n=m;            m=temp;        }        k=n-m;        n=(int)(k*(1.0+sqrt(5.0))/2.0);        if(n==m)         printf("0\n");        else         printf("1\n");    }    return 0;}

HDU2177:前一题的升级版
#include<stdio.h>#include<string.h>#include<math.h>#define MAX 99999999 int main(){int n,m,t,i;double q;q=(sqrt(5.0)+1)/2.0;while(~scanf("%d%d",&n,&m)&&(n||m)){t=(int)(q*(m-n)); if(n!=t){printf("1\n");printf("%d %d\n",t,m-(n-t)); int x;for(i=0;i<=m;i++){x=m-i; if(x>n) {if( (int)(q*(x-n))==n ) printf("%d %d\n",n,x);}else if(x<n){if( (int)(q*(n-x))==x )printf("%d %d\n",x,n);}}}else{printf("0\n");}}}

理解了Wythoff的规则这种题还是很水的

(三)尼姆博奕Nimm Game):





0 0
原创粉丝点击