博弈论?【转载】

转自:https://www.cnblogs.com/sssy/p/8485307.html

巴什博奕:

两个顶尖聪明的人在玩游戏,有n个石子,每人可以随便拿1−m个石子,不能拿的人为败者,问谁会胜利

结论:

设当前的石子数为n=k∗(m+1)

即n

时先手一定失败
HDU1846

#include<iostream>
using namespace std;
int main() {
    int C,N,M;
    scanf("%d",&C);
    while(C--) {
        scanf("%d%d",&N,&M);
        if(N%(M+1)==0) printf("second\n");
        else printf("first\n");
    }
    return 0;
}

HDU4764

#include<cstdio>
#include<algorithm>
const int MAXN=1e6+10,INF=1e9+10;
int main() {
    int x,y;
    while(scanf("%d%d",&x,&y)) {
        if(!x&&!y) break;
        if( (x-1)%(y+1)==0 ) puts("Jiang\n");
        else puts("Tang\n");
    }
    return 0;
}

nim游戏:

有两个顶尖聪明的人在玩游戏
有n堆石子,两个人可以从任意一堆石子中拿任意多个石子(不能不拿),没法拿的人失败。问谁会胜利

结论:

当n

堆石子的数量异或和等于0

时,先手必败,否则先手必胜

简证:

定义当前状态为
P-position:在当前的局面下,先手必败
N-position:在当前的局面下,先手必胜
对于面对局面为
0 xor 0 xor ... 0=0

称它为p局面

若当前局面为
a1 xor a2 ...an=k

来说
我们可以通过改变一个ai的比如ai xor k值来使得上式的值为0,此时修改该操作的人为必败局面,由于xor计算的特殊性,我们知道一定有一个ai最高位与k最高位的1是相同的,那么必然有ai xor k <ai

的,所以操作可为减法

对于局面
a1 xor a2 ...an=0

那么此时对于任何一种操作都会使得上式的值发生改变,也就是不为0
对与xor的性质,当每个位置的1都存在偶数个时,异或和为0,只改变一个ai,一定会使得某个位置的1的个数发生改变
所以只有当先手面临xor

和为0是,必胜
luogu P2197 nim游戏

#include<cstdio>
#include<iostream>
#include<algorithm>
int main() {
    int T;
    //scanf("%d",&T);
    std::cin>>T;
    for(int n;T--;) {
        std::cin>>n;
        int tmp=0;
        for(int q,i=1;i<=n;++i) {
            std::cin>>q;tmp^=q;
        }
        if(!tmp)puts("No");
        else puts("Yes");
    }
    //char a[10];scanf("%s",a);puts(a);
    return 0;
}

SG函数

SG函数内容的理解借鉴自zyf学长,鸣谢!QUQ
对于nim游戏,你已经知道怎么做了
但是,要是稍加改变游戏规则,那么恐怕无法很快找出必胜策略

我们可以吧游戏抽象为一张图,每一个点代表一个状态,对于每个状态与他的子状态连一条有向边。那么,我们在这张有向无环图上引入SG函数

定义

定义运算mex()

,这是对于一个集合的运算,表示最小的不属于该集合的非负整数
例如mex{0,1,2,3}=4;mex{2,3,5}=0。
那么对于每个顶点的SG值为:
后继中未出现过的最小值

sg(x)=mex{sg(y)}

y为x的后继

SG函数的性质

首先对于出度为0的点,他的SG值为0
对于一个SG值为0的点x,他的后继y都满足sg(y)!=0,y的后继中一定有存在一个点的SG值为0
诶,是不是和刚才nim游戏很像
所以当SG(x)=0时,x代表了p局面

扩展

考虑有向图上有n个棋子,每次可以移动一颗,不能移动者lose,这时候怎样找到必胜策略呢?
我们来考虑每个顶点的SG值得含义,当sg(x)=k

时,x的后继y的sg(y)=1 −> k也就是说,某个棋子进行移动后sg值是一定会发生改变的
类比普通的nim游戏,Nim游戏的一种必胜策略就是把ai

变为k,
那么,把第i个棋子移动到一个SG值为k的点上就是一种必胜策略
hhhh,也就是说Nim游戏的一种必胜策略对应着这n个棋子的必胜策略

  • 那么这个游戏P局面就棋子所在位置的SG函数异或和为0的时候。

经典的博弈问题

再来说一下几类经典的博弈问题

Anti-Nim游戏和SJ定理

有两个顶尖聪明的人在玩游戏(他们一个是崩崩崩玩家一个是舰娘玩家),游戏规则是这样的:
有n堆石子,两个人可以从任意一堆石子中拿任意多个石子(不能不拿),拿走最后一个石子的人失败。问谁会胜利?

结论

先手必胜当且仅当:
所有堆的石子数都为1且游戏的SG值为0
有些堆的石子数大于1且游戏的SG值不为0

证明

1)游戏有三种情况
每堆只有一个石子

  • 异或和为0先手必胜
  • 异或和不为0先手必败
    2)只存在一堆石子数>1,则先手必胜
    可以发现,在这种情况下异或值一定 != 0
    并且先手一定可以使剩下的奇数个石子变为个数为1的堆
    3)存在2堆以上的石子数>1
    -异或和=0时先手必败
    -异或和!=0先手必胜
    那么
    考虑nim游戏,当异或和=0时,洗衣不操作都会使异或和!=0,相反同样....符合NP状态的转换
    不断进行转换会在一个时刻变为 ‘只有一堆大于1‘ 的局面,而该局面一定是有 ‘两堆>1且异或和=0’ 的局面 转来的,所以 ‘两堆>1且异或和=0’ 的局面一定会在某一时刻转化为 ‘只有一堆大于1’ 的 先手必胜局面 ,也就是说是先手必败态;
    其他同理可证
    bzoj 1022: [SHOI2008]小约翰的游戏John
#include<cstdio>
#include<algorithm>
inline int read() {
    int x=0,f=1;
    char c=getchar() ;
    while(c<‘0‘||c>‘9‘)  {
        if(c==‘-‘)f=-1;
        c=getchar();
    }
    while(c<=‘9‘&&c>=‘0‘)x=x*10+c-‘0‘,c=getchar();
    return x*f;
}
int a;
int main() {

    for(int n,T=read();T--;) {
        n=read();bool op=1;int tmp=0;
        for(int i=1;i<=n;++i) {
            a=read();if(a!=1)op=0;
            tmp^=a;
        }
        if((op&&!tmp) || (!op&&tmp))puts("John");
        else puts("Brother");
    }
    return 0;
}

SJ定理

定理:SJ定理

对于任意一个Anti-SG游戏,如果我们规定当局面中所有的单一游戏的SG值为0时,游戏结束,则先手必胜当且仅当:

(1)游戏的SG函数不为0且游戏中某个单一游戏的SG函数大于 1; (2)游戏的SG函数为0且游戏中没有单一游戏的SG函数大于1。

Multi-SG

有n堆石子,两个人可以从任意一堆石子中拿任意多个石子(不能不拿)或把一堆数量不少于2石子分为两堆不为空的石子,没法拿的人失败。问谁会胜利

分析

用sg函数来解决
操作1与nim游戏无异,对与一个石子数为k的点来说,后继可以为1..k
操作二实际上是将一个游戏分解为两个游戏,根据SG定理,两个有的的和为两个游戏的SG函数值得异或,我们可以通过异或运算把两个单一游戏连接到一起,作为一个后继状态
要是数据范围很大,SG函数就不能用了,有结论

SG(x)=???x−1(xmod4=0)x(xmod4=1∨2)x+1(xmod4=3)

hhhhhhhhhhh

定义

根据上面的游戏,定义Multi-SG游戏

Multi-SG 游戏规定,在符合拓扑原则的前提下,一个单一游戏的后继可以为多个单一游戏。

  • Multi-SG其他规则与SG游戏相同。注意在这里要分清楚后继与多个单一游戏

    例题

    HDU 3032

    #include<cstdio>
    #include<cstring>
    using namespace std;
    const int maxn=1001;
    int read() {
    char c=getchar();int x=0,f=1;
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){x=x*10+c-‘0‘;c=getchar();}
    return x*f;
    }
    int a[maxn],sg[maxn];
    int main() {
    int T=read();
    while(T--) {
        int n=read();
        for(int i=1;i<=n;i++) a[i]=read();
        for(int i=1;i<=n;i++)
            if(a[i] % 4 == 0) sg[i] = a[i]-1;
            else if(a[i]%4==1||a[i]%4==2) sg[i] = a[i];
            else sg[i] = a[i]+1;
        int ans=0;
        for(int i=1;i<=n;i++)
            ans^=sg[i];
        if(ans)puts("Alice");
        else  puts("Bob");
    }
    return 0;
    }

    POJ 2311

    #include<cstdio>
    #include<cstring>
    const int maxn=3824;
    int read() {
    char c=getchar();int x=0,f=1;
    while(c<‘0‘||c>‘9‘){if(c==‘-‘)f=-1;c=getchar();}
    while(c>=‘0‘&&c<=‘9‘){x=x*10+c-‘0‘;c=getchar();}
    return x*f;
    }
    int sg[maxn][maxn];//当前剩余i行 j列的sg函数
    int vis[maxn];
    int main() {
    int n=201,m=201;
    for(int i=2;i<=n;i++)
        for(int j=2;j<=n;j++) {
            memset(vis,0,sizeof(vis));
            for(int k=2;k<=i-2;k++) vis[sg[k][j]^sg[i-k][j]]=1;
            for(int k=2;k<=j-2;k++) vis[sg[i][k]^sg[i][j-k]]=1;
            for(int k=0;;k++) if(!vis[k]) {sg[i][j]=k;break;}
        }
    while(scanf("%d%d",&n,&m)!=EOF)
        puts(sg[n][m] ? "WIN" : "LOSE");
    return 0;
    }

    BZOJ 2940

BZOJ 1188

洛谷 3235

Every-SG

给定一张无向图,上面有一些棋子,两个顶尖聪明的人在做游戏,每人每次必须将可以移动的棋子进行移动,不能移动的人输

分析

这样的话,能赢得游戏必须赢
为了赢得最后的胜利
当你知道某一个单一游戏一定会输的话,你需要尽力缩短游戏的时间,相反的当你知道某一个游戏一定会赢的话,那么就需要尽力延长游戏的时间

定义:

  • 对于还没有结束的单一游戏,游戏者必须对该 游戏进行一步决策;
  • Every-SG游戏的其他规则与普通 SG游戏相同

Every-SG游戏与普通游戏最大的不同就是他多了一维时间
对于SG的值为0,我们需要周到最少走多少步才能结束,对于SG值不为0的点我们需要最多走多少步结束
我们用step变量来记录这个步数

step(u)=???0,max{step(v)},min{step(v)},u为终止状态 sg(u)≠0∧v为u的后继∧sg(v)=0 sg(u)=0∧v为u的后继

定理

对于Every-SG游戏先手必胜当且仅当单一游戏中最大的step为奇数。
定理是显然的:

对于Every-SG游戏先手必胜当且仅当单一游戏中最大的step为奇数。

定理是显然的:最大的单一游戏步数如果是奇数的话那么肯定是先手取得进行最后一步,否则一定是对手取走最后一个棋子。
HDU 3595 GG and MM

balabala//我还没写QUQ

原文地址:https://www.cnblogs.com/hyghb/p/9065558.html

时间: 2024-10-07 14:41:29

博弈论?【转载】的相关文章

博弈论类题目小结——转载

出处http://blog.csdn.net/ACM_cxlove?viewmode=contents    by---cxlove 首先当然要献上一些非常好的学习资料: 基础博弈的小结:http://blog.csdn.net/acm_cxlove/article/details/7854530 经典翻硬币游戏小结:http://blog.csdn.net/acm_cxlove/article/details/7854534 经典的删边游戏小结:http://blog.csdn.net/acm

【博弈论的总结】【转载】

转自:http://www.cnblogs.com/celia01/archive/2011/11/15/2250171.html [以下内容为转载] 上次做poj 1067的取石子游戏,只用到了whthoff博弈,未涉及到取石子的异或方法,今天重新搜索,整理了一遍.搜罗各种资料,加上自己整理,终于成篇啦!……噼里啪啦 取石子问题 有一种很有意思的游戏,就是有物体若干堆,可以是火柴棍或是围棋子等等均可.两个人轮流从堆中取物体若干,规定最后取光物体者取胜.这是我国民间很古老的一个游戏,别看这游戏极

转载:hdu 题目分类 (侵删)

转载:from http://blog.csdn.net/qq_28236309/article/details/47818349 基础题:1000.1001.1004.1005.1008.1012.1013.1014.1017.1019.1021.1028.1029. 1032.1037.1040.1048.1056.1058.1061.1070.1076.1089.1090.1091.1092.1093. 1094.1095.1096.1097.1098.1106.1108.1157.116

转载:poj题目分类(侵删)

转载:from: POJ:http://blog.csdn.net/qq_28236309/article/details/47818407 按照ac的代码长度分类(主要参考最短代码和自己写的代码) 短代码:0.01K–0.50K:中短代码:0.51K–1.00K:中等代码量:1.01K–2.00K:长代码:2.01K以上. 短:1147.1163.1922.2211.2215.2229.2232.2234.2242.2245.2262.2301.2309.2313.2334.2346.2348

【转载】C++拷贝构造函数(深拷贝,浅拷贝)

对于普通类型的对象来说,它们之间的复制是很简单的,例如:int a=88;int b=a; 而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量.下面看一个类对象拷贝的简单例子. #include <iostream>using namespace std;class CExample {private:     int a;public:     CExample(int b)     { a=b;}     void Show ()     {        cout<

门控时钟-理论分析 ---- 转载

转载自:http://www.chipsbank.com/news_detail/newsId=123.html 门控的基本要求: 1. 所需要的沿(对于正沿触发的寄存器是正沿,对于负沿触发的寄存器是负沿)不增加,不减少: 1. 不会产生毛刺: 1. 使用后功耗要能够降低: 1. 最好面积还会减小. 1. 上升沿触发的门控时钟的结构研究:应用与上升沿触发的寄存器的门控. 1. 直接与门结构: 1. 高电平使能Latch + 与门结构: 1. 低电平使能Latch + 与门结构: 1. 波形研究:

浅谈Java中的equals和==(转载)

在初学Java时,可能会经常碰到下面的代码: 1 String str1 = new String("hello"); 2 String str2 = new String("hello"); 3 4 System.out.println(str1==str2); 5 System.out.println(str1.equals(str2)); 为什么第4行和第5行的输出结果不一样?==和equals方法之间的区别是什么?如果在初学Java的时候这个问题不弄清楚,就

JVM学习(2)——技术文章里常说的堆,栈,堆栈到底是什么,从os的角度总结--转载http://www.cnblogs.com/kubixuesheng/p/5202561.html

转载自---http://www.cnblogs.com/kubixuesheng/p/5202561.html 俗话说,自己写的代码,6个月后也是别人的代码--复习!复习!复习!涉及到的知识点总结如下: 堆栈是栈 JVM栈和本地方法栈划分 Java中的堆,栈和c/c++中的堆,栈 数据结构层面的堆,栈 os层面的堆,栈 JVM的堆,栈和os如何对应 为啥方法的调用需要栈 属于月经问题了,正好碰上有人问我这类比较基础的知识,无奈我自觉回答不是有效果,现在深入浅出的总结下: 前一篇文章总结了:JV

GitHub超详细图文攻略 - Git客户端下载安装 GitHub提交修改源码工作流程 Git分支 标签 过滤 Git版本工作流(转载)

最近听同事说他都在使用GitHub,GitHub是程序员的社区,在里面可以学到很多书上学不到的东西,所以最近在准备入手这方面的知识去尝试学习,正好碰到这么详细完整的文章,就转载了,希望对自己和大家有帮助. GitHub操作总结 : 总结看不明白就看下面的详细讲解. GitHub操作流程 : 第一次提交 : 方案一 : 本地创建项目根目录, 然后与远程GitHub关联, 之后的操作一样; -- 初始化Git仓库 :git init ; -- 提交改变到缓存 :git commit -m 'desc