博弈论算法

博弈游戏特点:

1、博弈模型为两人轮流决策的非合作博弈。即两人轮流进行决策,并且两人都使用最优策略来获取胜利。

2、博弈是有限的。即无论两人怎样决策,都会在有限步后决出胜负。

3、公平博弈。即两人进行决策所遵循的规则相同。

理论基础:

1、定义P-position和N-position:其中P代表Previous,N代表Next。直观的说,上一次move的人有必胜策略的局面是P-position,也就是“先手必败”,现在轮到move的人有必胜策略的局面是N-position,也就是“先手可保证必胜”。

(1).无法进行任何移动的局面(也就是terminal position)是P-position;

(2).可以移动到P-position的局面是N-position;

(3).所有移动都导致N-position的局面是P-position。

2、P/N状态有如下性质:

(1)、若面临末状态者为获胜则末状态为胜态否则末状态为必败态。
(2)、一个局面是胜态的充要条件是该局面进行某种决策后会成为必败态。
(3)、一个局面是必败态的充要条件是该局面无论进行何种决策均会成为胜态
3、P点: 即必败点,某玩家位于此点,只要对方无失误,则必败;

N点: 即必胜点,某玩家位于此点,只要自己无失误,则必胜。

*4、取石子游戏算法实现

步骤1:将所有终结位置标记为必败点(P点);

步骤2: 将所有一步操作能进入必败点(P点)的位置标记为必胜点(N点)

步骤3:如果从某个点开始的所有一步操作都只能进入必胜点(N点) ,则将该点标记为必败点(P点) ;

步骤4: 如果在步骤3未能找到新的必败(P点),则算法终止;否则,返回到步骤2

常见博弈类型:

一、巴什博弈    

        1、问题模型:只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个,最后取光者得胜。

        2、解决思路:当n=m+1时,由于一次最多只能取m个,所以无论先取者拿走多少个,后取者都能够一次拿走剩余的物品,后者取胜,所以当一方面对的局势是n%(m+1)=0时,其面临的是必败的局势。所以当n=(m+1)*r+s,(r为任意自然数,s≤m)时,如果先取者要拿走s个物品,如果后取者拿走x(≤m)个,那么先取者再拿走m+1-k个,结果剩下(m+1)(r-1)个,以后保持这样的取法,那么先取者肯定获胜。总之,要保持给对手留下(m+1)的倍数,就能最后获胜。

        3、变形:条件不变,改为最后取光的人输。

        结论:当(n-1)%(m+1)==0时后手胜利。

二:在一堆物品中取东西,但是并非取1-m个,也许是间段取,或者幂次方取,思路是暴力,0是p点,从0往后暴力枚举,后面的点只要有一个点取走k个之后能到达p点那这个点就是N点,否则是P点,eg:

1、  总共n张牌;
2、  双方轮流抓牌;
3、  每人每次抓牌的个数只能是2的幂次(即:1,2,4,8,16…)
4、  抓完牌,胜负结果也出来了:最后抓完牌的人为胜者;
假设Kiki和Cici都是足够聪明(其实不用假设,哪有不聪明的学生~),并且每次都是Kiki先抓牌,请问谁能赢呢?

1 3

Kiki Cici

int m[12]={1,2,4,8,16,32,64,128,256,512},s[1005];
int main()
{
    s[0]=0;
    for(int i=1;i<=1000;i++){
        for(int j=0;j<10;j++){
            if(i-m[j]>=0){
                if(s[i-m[j]]==0){
                    s[i]=1;
                    break;
                }
            }
        }
    }
    while(scanf("%d",&n)!=EOF){
        if(s[n]==1){
            printf("Kiki\n");
        }else{
            printf("Cici\n");
        }
    }

三:NiM尼姆博弈

有三堆各若干个物品,两个人轮流从某一堆取任意多的物品,

规定每次至少取一个,多者不限,最后取光者得胜。

结论:如果a^b^c=0,那么先手必输,否则先手必胜。

证明如下:

这种情况最有意思,它与二进制有密切关系,我们用(a,b,c)表示某种局势,首
先(0,0,0)显然是奇异局势,无论谁面对奇异局势,都必然失败。第二种奇异局势是
(0,n,n),只要与对手拿走一样多的物品,最后都将导致(0,0,0)。仔细分析一
下,(1,2,3)也是奇异局势,无论对手如何拿,接下来都可以变为(0,n,n)的情
形。

    计算机算法里面有一种叫做按位模2加,也叫做异或的运算,我们用符号(+)表示
这种运算。这种运算和一般加法不同的一点是1+1=0。先看(1,2,3)的按位模2加的结
果:

1 =二进制01
2 =二进制10
3 =二进制11 (+)
———————
0 =二进制00 (注意不进位)

    对于奇异局势(0,n,n)也一样,结果也是0。

    任何奇异局势(a,b,c)都有a(+)b(+)c =0。

如果我们面对的是一个非奇异局势(a,b,c),要如何变为奇异局势呢?假设 a < b
< c,我们只要将 c 变为 a(+)b,即可,因为有如下的运算结果: a(+)b(+)(a(+)
b)=(a(+)a)(+)(b(+)b)=0(+)0=0。要将c 变为a(+)b,只要从 c中减去 c-(
a(+)b)即可。
版权声明:本文为CSDN博主「木每立兄豪」的原创文章.
原文链接:https://blog.csdn.net/qq_43472263/article/details/99879891

推广二:当石子堆数为n堆时,则推广为当对每堆的数目进行亦或之后值为零是必败态。

SG函数:

首先定义mex(minimal excludant)运算,这是施加于一个集合的运算,表示最小的不属于这个集合的非负整数。例如mex{0,1,2,4}=3、mex{2,3,5}=0、mex{}=0。

那么计算1~n的SG函数值步骤如下:

1、使用 数组f 将 可改变当前状态 的方式记录下来。

2、然后我们使用 另一个数组 将当前状态x 的后继状态标记。

3、最后模拟mex运算,也就是我们在标记值中 搜索 未被标记值 的最小值,将其赋值给SG(x)。

4、我们不断的重复 2 - 3 的步骤,就完成了 计算1~n 的函数值。

vis[]数组是用来标记当前x节点的后节点是否被用过
for(int i=0;i<=1000;i++){
        memset(vis,0,sizeof(vis));
        for(int j=0;f[j]<=i;j++){//对于不同的题目,选取的数是不同的,这里的F数组是每次可以选取的
            vis[SG[i-f[j]]]=1;
        }
        for(int j=0;j<=1000;j++){
            if(!vis[j]){
                SG[i]=j;
                break;
            }
        }
    }

你可能感兴趣的:(ACM算法,算法)