组合博弈入门算法笔记

5 篇文章 0 订阅

组合博弈入门算法笔记

取子游戏

在这里插入图片描述
什么是组合游戏——

  1. 有两个玩家;
  2. 游戏的操作状态是一个有限的集合(比如:限定大小的棋盘);
  3. 游戏双方轮流操作;
  4. 双方的每次操作必须符合游戏规定;
  5. 当一方不能将游戏继续进行的时候,游戏结束,同时,对方为获胜方;
  6. 无论如何操作,游戏总能在有限次操作后结束。

概念:必败点和必胜点(P点&N点)
必败点(P点):前一个选手(Previous player)将取胜的位置成为必败点。即下一个选手必败。
必胜点(N点):下一个选手(Next player)将取胜的位置成为必胜点。即下一个选手必胜。

取子游戏算法实现——
**步骤1:**将所有终结位置标记为必败点(P点);
**步骤2:**将所有一步操作能进入必败点(P点)的位置标记为必胜点(N点);
**步骤3:**如果从某个点开始的所有一步操作都只能进入必胜点(N点),则将该点标记为必败点(P点);
**步骤4:**如果在步骤3未能找到新的必败点(P点),则算法终止;否则,返回到步骤2.

Nim游戏简介

1、有两个玩家;
2、有三堆扑克牌(比如:分别是5,7,9张);
3、游戏双方轮流操作;
4、玩家的每次操作是选择其中某一堆牌,然后从中取走任意张;
5、最后一次取牌的一方为获胜方;

引入概念Nim-Sum

定理一:
对于nim游戏的某个位置(x1,x2,x3),当且仅当它各部分的nim-sum等于0时(即x1^ x2^x3 = 0),则当前位于必败点。
定理一也适用于更多堆的情况~

SG函数
sg(x) = min { n >= 0 : n <> sg(y) for y ∈F(x) }
也就是说,x节点的SG值是去除x的后继节点的SG值后的最小非负整数。

SG函数的使用
必败点:当前节点x的sg(x) = 0
必胜点:当前节点x的sg(x) > 0

定理二:
如果图游戏G由若干子图游戏Gi组成,即:
G = G1 + ··· +Gn,假设gi是Gi(i = 1, … , n)的SG函数值,那么图游戏G的SG值计算如下:
g(x1, … , xn) = g1(x1) ^ ··· ^gn(xn)

例题:
在这里插入图片描述
AC代码:

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
int k,a[100],f[10001];//k种规则,a[]保存每次取几张牌,f[]保存后继节点的sg数

int sg(int p)
{
    int t;
    bool g[101] = {0};//标记后继节点的sg数
    for(int i = 0; i < k; i++)
    {
        t = p - a[i];
        if(t < 0)
        {
            break;
            //continue;
        }
        if(f[t] == -1)  f[t] = sg(t);//之前没有保存后继节点的sg值
        g[f[t]] = 1;//标记
    }
    for(int i = 0; ; i++)
    {
        if(!g[i])
        {
            return i;//sg取值,去除后继节点的最小的非负整数
        }
    }
}

int main()
{
    int n,i,m,t,s;//n组数据,m堆排 
    while(scanf("%d",&k) == 1 && k)
    {
        //if(k == 0)  break;
        for(i = 0; i < k; i++)
        {
            scanf("%d",&a[i]);
        }
        sort(a,a+k);
        memset(f,-1,sizeof(f));
        f[0] = 0;//初始化终结点
        scanf("%d",&n);//n组数据 
        while(n--)
        {
            scanf("%d",&m);//m堆牌 
            s = 0;
            while(m--)
            {
				scanf("%d",&t);
	            if(f[t] == -1)
	            {
	                f[t] = sg(t);
	            }
	            s = s ^ f[t];
	        }
            if(s == 0)
		        printf("L");
		    else
		        printf("W");
        }
        printf("\n");
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值