其他分享
首页 > 其他分享> > Game Theory

Game Theory

作者:互联网

Game Theory

目录

博弈的基本概念

组合游戏

SG函数

经典组合游戏模型

导言:博弈的基本概念

博弈论是研究具有斗争和竞争性质现象的数学理论和方法,博弈论,又称为对策论(Game Theory)、赛局理论等,既是现代数学的一个新分支,也是运筹学的一个重要学科。

博弈论主要研究公式化了的激励结构间的相互作用,是研究具有斗争或竞争性质现象的数学理论和方法。博弈论考虑游戏中的个体的预测行为和实际行为,并研究它们的优化策略。生物学家使用博弈理论来理解和预测进化论的某些结果。

数学化的表示为,假设玩家的集合是N,第i个玩家会有策略集合Σi,游戏最终结果为X,如果每个玩家使用不同的策略,最后产生了游戏的结果,可以形式化的表达为

\[\prod_{I\in N} \sum i->X \]

每个玩家对结果有不同的偏好,用一个偏好函数来描述,所有玩家的最优策略一般可以看作一个优化问题,使得每个偏好策略乘起来造成最优的结果。

一般的博弈问题都很难以解决。所以在一般抽象出的问题中。我们都会简化策略,在算法竞赛中,我们往往会先把问题简化为两个人之间进行博弈在一些规则下进行博弈,并求出在最优策略下游戏最终的结果走向。

我们先通过一个简单问题来认识一下博弈游戏

例1: Paindrome Game[CF 1527 B2]

给一个长度为n的01串S,Alice和Bob玩游戏。两人轮流进行以下两种操作中的一种:

1.选择任何一个s[i]='0',把他修改为'1',支付1块钱

2.将串翻转,不需要支付钱,此操作只能在当前串是非回文串,且上一次操作不是翻转的情况下选择,

当S为全1串,游戏结束,如果Alice花费小于Bob则Alice嬴否则Bob嬴相等就,平局,给定字符串,问最优策略下谁会嬴。t组数据(t,n<=1e3)

思考问题

首先我们观察问题,参与游戏的玩家是Alice和Bob,Alice的游戏策略会倾向于尽量让A<B,Bob会尽量倾向让B<A.

那么我们先考虑当串初始状态就是回文串的情况,这种情况下,如果只有中间一个0,bob可以获胜,否则如果0的数量为奇数,那么alice总可以先填中间把状态转换为0是偶数且现在是回文的情况,花费代价1,而这种情况,先手一定会落后2个代价,也就是这个状态是一个P态,至于原因,我们举个简单的例子,现在如果是00100,先手方必须先把一个0变成1,此时如果这不是最后一对,我们就模范先手,在对称的位置同样操作,也就是00100->01100->01110,最后一对时 ,先手在变换,我们选择翻转,先手必须在转一次,需要花费2的代价。所以只有cnt为1或者现在就0个数为偶数的情况bob才能获胜。

然后在看不是回文的状态,很容易发现,如果现在不是回文,除了少数情况,A总能通过不断翻转节约大量费用。B只能努力构建回文串。然后如果构建回文的过程代价超过一定值,就算现在是回文状态,A仍然可以通过上述方法获胜。只有刚好0的数量是2且只有一个位置回文不对称时,又可能平局,也就是类似01011的情况,Alice只能先选第一个,然后B补全第二个才能造成平局这种情况。如果不相等的数超过1的这种情况,那么B要么一直填到回文alice在反应,要么也可以在最后一个之前自己把串变成回文,让b变成必败态,也就是说A总掌握先手,而不相等数等于1的情况。就算0000001这种情况alice只需要一直旋转,bob就算搞成回文串,也不能改变局面。因为变为回文后,A只要填中间就还是把必败态给了B,除非上文说的01011这种情况可以平局。而如果中间是1,0001001A开局直接把串变成回文就能赢。综上不是回文的时候,只有一个特殊情况可以平局

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
ll t,n;
string s;
int main()
{
    cin.tie(nullptr)->sync_with_stdio(false);
    cin>>t;
    while(t--)
    {
        cin>>n;
        cin>>s;
        ll cnt=0,hui=0;
        for(int i=0;i<n;i++)
        {
            if(s[i]=='0')cnt++;
        }
        for(int i=0;i<n/2;i++)
        {
            if(s[i]!=s[n-i-1])hui++;
        }
        if(hui)
        {
            if(cnt==2&&hui==1)cout<<"DRAW"<<"\n";
            else cout<<"ALICE"<<'\n';
        }
        else {
             if((cnt%2)==0||cnt==1)cout<<"BOB"<<"\n";
            else cout<<"ALICE"<<"\n";
        }
    }   
    return 0;
}

组合游戏

在算法竞赛中,一般考察的问题都是组合游戏(combinatorial game)组合游戏有几个性质

1.两个玩家进行

2.有一个状态集合

3.游戏规则指明玩家在一个状态下可以移动到其他那些状态

4.玩家轮流行动

5.如果当前的状态,玩家根据规则无法移动,游戏会结束

6.无论玩家如何选择,游戏会在有限步内结束(也就是,所有状态构成的集合,可以形成一个有向无环图)

在本文中,绝大多数内容都是组合游戏的一种,就算不是组合游戏,往往也可以用分析组合游戏的方法分析。刚刚的例题就不严格是一个的组合游戏,规则不完全公平,

下面我们介绍一些最经典的组合游戏问题

Bash博弈

有n个石子,Alice和Bob轮流取每次最少取1个,最多取y个,如果轮到一个玩家时,他无法取式子,则他输。

这是一个非常经典博弈问题。在介绍解决这个问题之前,我们先介绍几个简单概念。

P状态(previous position)意思是前一个走到这个状态的玩家可以赢的状态。

N状态(next position)意思是当前状态往后走的可以赢的状态。

关于PN态,这里给出两个最重要的性质

至少能走到一个P态的状态为N态。

只能走到N态的状态为P态。

一般解决这类问题,我们往往会从终点态开始往会推。

在这里我们先假设y为3来尝试分析一下问题的性质,为了解决这种问题,我们常用的一个手段是先打表看出一部分规律在进一步观察性质。

对于Bash博弈,终点态也就是0.如果一个玩家走到0他就会嬴,或者一个玩家从0开始走他会输,也就是说0是1个P状态。然后向前推,容易发现1,2,3都可以通过一次拿取转移到P状态。也就是说他们可以转移到一个可以嬴的状态。所以他们属于当前往后走可以获胜的状态,所以他们都属于N状态,然后4不管这么走,都只能走到N状态,那么他就是P状态了。

0(P态) 1(N态) 2(N态) 3(N态) 4(P态) 5(N态) 6(N态) 7(N态) 8(P态) 9(N态)

不难发现,规律是按一定周期循环的。我们可以先大胆猜测一个结论 当n%(1+Y)==0时的状态就是一个P状态。 然后验证他的正确性。对于这个问题,其实可以通过概念来验证结论,每个(1+Y)看作一个周期。距离上一个P态Y以内的点都是N态。第1+Y个状态是P态因为他只能走到N态。之后就不断循环即可。

总结下,其实P状态就是当前玩家会输的状态,N状态就是当前玩家会嬴的状态。

一般问题先看结束状态时P状态还是N状态,然后不断求出所有状态

正常规则和反常规则

正常规则(normal play rule):终态为P态(无法走的人输)

反常规则(misere play rule):终态为N态(无法走的人赢)

在实际问题中,根据题目要求确定规则,在求解问题。

更常规的组合游戏

Bash博弈是一种非常典型的组合博弈游戏。其状态也比较好观察。接下来我们在用几个例题来进一步看看从Bash博弈中学到的技巧在更多同类问题中的应用。

例1: hdu1404

题意:一串由0~9组成的数字,可以进行两个操作:

1、把其中一个数变为比它小的数;

2、把其中一个数字0及其右边的所以数字删除。

两人轮流进行操作,最后把所以数字删除的人获胜,问前者胜还是后者胜。

T组数据(T<10^5)每个字符串长度不超过6.

首先最后删除的人获胜,所以是正常规则,终态是P态。从终点状态出发分析。容易发现,P态到N态之间的转移比较明显,且数据范围在1e6以内,我们可以通过hash技术来处理字符串。然后打表求出所有状态现在所处的状态。直接O(1)判断即可。

打表可以dfs也可以直接枚举扩散。按照PN态转换规则打表即可。题目有一个重要性质是操作会让数渐渐变小。所以如果小的P状态都无法转移到大的状态。说明能到这个大的状态的点只能被N态转移得到。那么他自然就是P态。利用这个性质我们可以默认所有状态都是P态。从P态出发,所有能到达P态的都是N态。反向看题中的条件。从小到大处理可以转移到P态的状态将其设置为N态即可。

这里给出一种朴素写法。打表方法有很多。大家选择自己喜欢的方法即可。

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
ll t;
ll f[1000010];
string s;
int getlen(int x)
{
    int cnt=0;
    while(x)
    {
        x/=10;
        cnt++;
    }
    return cnt;
}
void init()
{
    f[0]=1;//可以直接删除到终点 N态
    f[1]=0;//只能把一个数变为0。P态
    for(int i=1;i<1000000;i++)
    if(!f[i])//能到P态就是N态
    {
        ll len=getlen(i);
        ll tmp=i;//给每个位加数
        ll tmp2=1;
        for(int j=0;j<len;j++)
        {
            ll now=tmp%10;
            for(int k=1;now+k<=9;k++)
            f[i+k*tmp2]=1;
            tmp/=10;
            tmp2*=10;
        } 
        ll tmp1=i,now=1;//给后面加0和后缀
        while(len<6)
        {
            tmp1*=10;
            len++;
            for(int j=0;j<now;j++)
            f[tmp1+j]=1;
            now*=10;
        }
    }
}
int main()
{
    cin.tie(nullptr)->sync_with_stdio(false);
    init();   
    cin>>t; 
    while(t--)
    {
        cin>>s;
        if(s[0]=='0')
        {
            cout<<"Yes"<<'\n';
        }
        else
        {
            int num=stoi(s);
            if(f[num])  cout<<"Yes"<<'\n';
            else cout<<"No"<<'\n';
        }
    }
    return 0;
}

例2:小牛再战

​ 共有N堆石子,已知每堆中石子的数量,两个人轮流取石子,每次只能选择N堆石子中的一堆取一定数量的石子(最少取一个),取过子之后,还可以将该堆石子中剩余的石子随意选取几个放到其它的任意一堆或几堆上。等哪个人无法取子时就表示此人输掉了游戏。注意:一堆石子没有子之后,就不能再往此处放石子了。假设每次都是小牛先取石子,并且游戏双方都绝对聪明,现在给你石子的堆数、每堆石子的数量,请判断出小牛能否获胜。

这是一个正常规则的游戏。我们还是先从终点状态出发分析。终点显然是P态。那么所有只有一堆的情况都是N态,因为它可以转移到P态。接下来看有两堆的情况。我们假设两堆石子数量是\(x_1,x_2\),令\(x_1>=x_2\),容易发现如果相等,那么当前状态为p态,因为对手只需要不断保持两堆石子数相同即可,所以相等的情况为p态,那么\(x_1,x_2\)的可以转移为\(x_1=x_2\),所以为N态。扩展之后看3堆的情况,容易发现一定可以转化为\(x_1=x_2\)的状态所以,为N态。由此类推,4堆的状态中,如果可以两两对应那么就是P态,否则就是N态。奇数都是N态。当然上述结论只是一个直觉上的猜测。那么我们如何证明这个结论。

这里给出一种构造性的证明。对于N为奇数的情况。假设n堆石子分别为\(x_1>=x_2>=x_3>=....>=x_n\)我们尝试给出一种让他们始终可以转移到P态的方式。让他们变成\(x_2x_2x_4x_4....\)的偶数形式。那么只需要删除\(x_1\)给出合理分配即可。

也就是证明\(x_1>=x_2-x_3+x_4-x_5...+x_{n-1}-x_n\)然后将\(-x_3+x_4\)看作一个元素。变成\(x_1>=x_2+(-x_3+x_4)+(-x_5+x_6)....+(-x_n)\)所有括号里的数都小于等于0。又因为\(x_2<=x_1\)可以证明奇数堆总可以转移到一种P态。

对于偶数的情况。有一个类似的证明。我们还是假设n堆石子分别为\(x_1>=x_2>=x_3>=....>=x_n\)我们这次不能直接删除\(x_1\)所以把\(x_1\)变得和\(x_n\)凑对也就是让他们变为\(x_2x_2x_4x_4...x_nx_n\)那么就要证明\(x_1-x_n>=x_2-x_3+x_4-x_5...+x_{n-1}-x_n\)就类似的转换成

\(x_1>=x_2+(-x_3+x_4)+(-x_5+x_6)....+(-x_{n-1}+x_n)\)至此结论的证。

接下来的问题就很简单了

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
ll n;
ll a[1010];
int main()
{
    cin.tie(nullptr)->sync_with_stdio(false);
    while(cin>>n&&n)
    {
        ll tmp=0;
        ll fl=0;
        memset(a,0,sizeof(a));
        for(int i=1;i<=n;i++)
        {
            ll x;
            cin>>x;
            a[x]++;
        }
        for(int i=1;i<=100;i++)
        if(a[i]%2)fl=1;
        if(fl||n%2)cout<<"Win"<<"\n";
        else cout<<"Lose"<<"\n";
    }
    return 0;
}

思考题CF1537D

Alice 和 Bob 玩游戏,从正整数\(1 ≤ n≤ 10^9\) 开始,每次轮流操作,Alice 先手。每轮玩家

要把 n 减去一个不为 1 或 nn 的因子,如果不能操作则输。求谁赢。

类似的想法,先打表,1和所有的质数显然是p态,对于比较小的数大家可以打表得到

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
P P P N P N P P P N P N P N P N P N P N

观察数据,可能会有猜测,在n比较大后,奇数都是P态,偶数都是N态。首先因为奇数只有奇因子,所以奇数减奇数一定是偶数,只能转移到N态,但是在偶数处理的时候,我们需要考虑,是否他是否可以减去一个因子变成奇数,仔细观察,如果偶数是\(2^K\)的形式,他没有奇因子,无法转移,所以结论错误,但是容易发现,在除这种以为的偶数仍然满足性质。那么继续分析,观察2的次幂,容易发现对于2的幂次,2是p态,4是N态,8是P态,16是N态,那么继续给出进一步猜测,对于偶数,2的奇数次幂是P态否则就是N态。接下来,如果比赛可以不考虑证明,直接交,平时训练可以尝试证明这个问题。

对于新给出的结论。我们还是从奇数出发,奇数减一个奇数的因子,有没有可能变为P态呢,问题变为是否存在\(n-d=2^k\)提取因子发现到一定是奇因子,而等式右边不存在奇因子,所以一定不可能转移到P态,所以奇数仍然只能是P态。对于偶数。对于\(2^k\) k是奇数的情况可以转移到\(2^k-2^i\)只能走到N态而k是偶数,可以走到一个P态。所以结论得证

小结

对于常规的组合问题,我们往往会尝试从小数据出发,分析性质,打表猜测结论,如果比赛时候结论看上去比较正确可以不浪费时间证明。平时练习可以多证明练习。有时候练习的过程中也可以得到一些启发。

Nim游戏

假设有n堆石子,第i堆石子开始有\(x_i\)个,Alice和Bob轮流取石子,每次当前玩家可以从一堆石子中取出至少1个石子(至少取一个,可以全取完)。无法取石子的玩家输掉游戏。

我们还是按刚刚的方法简单分析一下这个问题。首先从终点出发,终点是P态,所有n=1的情况都可以转移到P态,所以都为N态,n=2的情况如果\(x_1=x_2\)那么只能转移到两个不等,后手只需要不断保证两个相等即可。而不等的时候总能转移到P态,所以不等是N态,相等是P态。如果更复杂的情况。其实不太好推断。只是看到这里可能很难猜出结论,这类游戏结论是一类比较经典的模型。所以我们先给出结论,在给出证明,从理解证明的角度出发来理解原因。

结论

P态当且仅当\(x_1\oplus x_2\oplus.....\oplus x_n=0\)

N态当且仅当\(x_1\oplus x_2\oplus.....\oplus x_n\neq 0\)

给出一个简单证明,还是从终点出发,终点显然满足异或和0.

对于任意异或和不为0的局面,我们总能找到一个石子堆,让他变小最后让异或和为0,也就是总能转移到P态。如何找的问题,就是从大到小第一个异或值不为0的位一定有一个石子堆这一位是1,我们就通过改变它的值,保证后续所有位都变为0即可。

对于任意异或和为0的局面,我们无论如何取石子,总会导致异或和不在等于零,只能转移到N态,所以为P态。

综上所述,由数学归纳法可证,结论成立。

反常Nim游戏

假设有n堆石子,第i堆石子开始有\(x_i\)个,Alice和Bob轮流取石子,每次当前玩家可以从一堆石子中取出至少1个石子(至少取一个,可以全取完)。无法取石子的玩家赢得游戏。

分析过程都类似,这个直接给出结论

如果所有石子堆石子数都为1,则与nim游戏规则相反,否则规则相同。具体分析过程可以同样方法尝试证明,这里不做赘述。

例1.hdu 1730 Northcott Game

Tom 和 Jerry 正在玩 Northcott 游戏。游戏在一个 nm 列(1 n 1000 且 2 m 100)的棋盘上进行,每行有一个黑(黑方)和一个白子(白方)。执黑的一方 先行,每次玩家可以移动己方的任何一枚棋子到同一行的任何一个空格上,当然这过程中 不许越过该行的敌方棋子。双方轮流移动,直到某一方无法行动为止,移动最后一步的玩 家获胜。Tom总是先下(黑方)。

思考观察,容易发现,这个游戏双方可以操作的对象是不一致的。不是严格的公平游戏,有很多状态是只有1方能到达的,但是进一步分析,会发现我们可以把它转换为一类公平游戏,也就是说有意义的状态本身不是黑棋和白棋的位置,而是黑棋和白旗的相对位置,因为他们之间的关系不会改变,我们只需要把相对距离看成石子堆,做nim游戏即可。一个区别在于这个游戏是可以退后的,实际上,对于所有退后操作,只需要跟他走同样步,迟早会把他逼到墙角,所以实质上,能改变状态的只有拉近双方距离,所以模型就变成了经典的nim游戏。代码这里就不给出了,大家自行完成。

Sprague-Grundy函数和Sprague-Grundy定理

关于SG函数,我们重点需要掌握他的基本原理和一些常用的做法,在算法竞赛涉及博弈论中占了半壁江山。(另外半壁江山是打表找规律)我们先从一些概念出发简单了解SG函数的定义和意义。

图游戏

给定一个有向图 G = (V, E), 其中 V 是非空的结点集,E 是有向边集。两个人在图 G 是 进行游戏。从起始结点 \(x_0\) 出发,轮流移动。每一轮当前玩家可以从当前结点 x 按照有向 边集移动到下一个结点,把 x 可以一步移动到的结点集合记为 F(x)。如果 F(x) 为空,则 x 为终止结点,此时玩家不能移动,游戏结束。(在正常游戏下不能移动的玩家输)

事实上,图游戏是一种组合游戏的抽象描述,一般性的组合游戏往往可以通过将其抽象成图。观察在图上的性质帮助分析问题。

SG函数

我们先给出SG函数的定义。

定义有向无环图 G = (V, E) 的 SG 函数是一个结点集 V 到非负整数 Z≥0 的映射\(g : V → Z≥0\)满足 \(g(x) = mex \left\{g(y) | y ∈ F(x)\right\}\)

需要注意的是这里我们把条件限制到了有向无环图中。返回到组合游戏本身,其实就是要求不存在一些状态可以自己来回转移,这样游戏就无法分出胜负,不在我们的讨论范畴之内。

然后介绍下mex的定义。

mex(S) 的值为集合S 中没有出现过的最小自然数。例如,mex(1,2) = 0、mex(0,1,2,3) = 4

随便举个例子

image

首先从定义出发,很容易发现,所有出度为0的节点的SG函数值都为0,这是因为它们没有后续节点的限制,我们可以从结尾开始,倒着根据后面节点的信息,处理出所有节点的SG函数值

首先终点态为4和6然后去掉这两个点,处理剩下出度为0的点。也就是2和3,那么2和3的SG函数值都为1,然后继续求出5的SG函数值为2,最后剩下的1节点由于后续出边没有SG函数为0的点,所以他的SG函数为mex(1,1,2)仍然为0.

到这里,我们应该了解了他的概念和求法,那么继续思考一个问题。每个点的SG函数值和PN态有什么关系呢?

还是从上面的图出发,终点4和6的状态显然是P态,能到达他们的点2,3,5显然是N态,1号点只能到达N态,所以1号点也就是P态。

我们可以根据性质,做出一个简单的猜测。


在正常游戏的状态下

x是P态当且仅当g(x)=0

x是N态当且仅当g(x)>0


事实上,从PN态的概念出发,也很容易看出这个性质。

其实看到这里,我们基本了解了SG函数的意义,但是还有一个问题没有解决,就是对于一个问题,如果我们能通过求出他的SG函数解决,一定也可以直接通过求PN态来解决,那么SG函数到底有什么意义呢?我们继续来看。

组合游戏的和

给定多个组合游戏,可以用如下方式把它们进行组合。每个游戏都有一个初始状态,每次 当前玩家可以选择一个游戏,并按照该游戏的规则移动一次,如果轮到当前玩家时,所有 游戏都不能移动,则该玩家输(正常规则)。称这个新的组合游戏为这些组合游戏的和。

举个最简单的例子,上文提到的nim游戏就是一个最经典组合游戏的和。

我们可以把nim游戏看作一个更简单的游戏,取石子游戏,每次可以从1堆石头中取大于等于1的任意个石子,将N个这样的游戏组合起来,就是经典的nim游戏。

所以说,很多比较简单的游戏都可以通过求和转化为复杂且有趣的新游戏。

那么这时候,简单的PN态分析就无法解决问题了。但是SG函数貌似也不太好求了,因为nim游戏中有n个小游戏,我们只能把每个小游戏抽象为图游戏,来计算他的SG函数。还是不能直接解决这个问题。

图游戏的和

假设给定 n 个有向无环图 \(G_i(V_i , E_i)\),则它们的图游戏的和 \(G(V, E)\) 满足

\(V = V_1 × V_2 × V_3 × ... × V_n\)(笛卡尔积)

结点 \(x = (x_1, x_2, ..., x_n)\)一步能到\(F(x) =\cup_{i = 1}^{n}=\left\{(x_1, x_2, ..., y_i , ..., x_n) | y_i ∈ F_i(x_i)\right\}\) 记 G 为\(G_1 + G_2 + ... + G_n\)

有了这些基础,接下来我们就介绍求解组合游戏问题的一大杀器SG定理

SG定理


假设有 n 个组合游戏\(G_1, G_2, ..., G_n\),记 \(g_i\) 为第 \(i (1 ≤ i ≤ n)\) 个组合游戏的 SG 函数,则它 们的和 $G = G_1 + G_2 + ... + G_n $的 SG 函数 \(g(x) = g((x_1, x_2, ..., x_n)) = g_1(x_1) ⊕ g_2(x_2) ⊕ ... ⊕ g_n(x_n)\)


先简单观察这个定理,其实就是nim游戏的结论,因为在nim游戏中,每个石子堆的SG函数就是这个石子堆的石子数。

我们尝试证明这个定理。

我们假设\(x=(x_1,x_2,...,x_n)\)。根据SG函数的定义,只需证明\(mex \left\{g(y)|y∈ F(x)\right\}\)包含所有小于\(A=g_1(x_1) ⊕ g_2(x_2) ⊕ ... ⊕ g_n(x_n)\)的非负整数,且A不属于这个集合。

我们先来证明前半部分,假设对于Z<A,一定存在某个\(y∈ F(x)\)满足\(g(y)=Z< g_1(x_1) ⊕ g_2(x_2) ⊕ ... ⊕ g_n(x_n)\)

方便大家理解,这里的证明不用详细的数学语言描述,尝试用一种通俗的方式帮助大家更好的理解他的原理。

我们按位来看每个sg函数的值,随便举个例子,这里的Z可以是任意小于A的值。

我们只需要找到从高到底,第一个不同的位.并且一定是A为1,那么因为这一位为1,代表g(x)中至少有1个子游戏的sg函数值这位为1,那么根据sg函数的定义\(g(x) = mex \left\{g(y) | y ∈ F(x)\right\}\) 我们一定可以走到任意一个比当前g(x)值小的状态,来让A转移到Z,至此问题的前半部分的证。

\(g(x_1)\)
\(g(x_2)\)
... 1
\(g(x_n )\)
结果
A 0 0 1 0 1 0 1 1
Z 0 0 1 0 0 1 1 0

我们继续看这个问题的下半部分,我们需要证明对于任意的\(y∈ F(x)\)满足\(g(y)=A\)

我们使用反证法,假设存在\(g_1(x_1) ⊕ g_2(x_2) ⊕ ... ⊕ g_n(x_n)<g_1(y_1) ⊕ g_2(y_2) ⊕ ... ⊕ g_n(y_n)\)根据异或的性质所有的\(g_i(x_i)=g_i(x_i)\) 根据SG函数的定义,这显然与假设不符,所以命题得证。


上面我们用一种比较好理解的角度给出了一种SG定理的证明。理解原理可以帮助大家更深刻的理解SG函数和SG定理的数学意义,实际上这个定理在某种程度上是利用的异或的一些特殊性质。构造性的给出了一类抽象问题的通解方法。

SG定理的推论


假设\(G=G_1+G_2+...+G_n\)

\(x=(x_1,x_2,...x_n)\)是P态当且仅当$ g_1(x_1) ⊕ g_2(x_2) ⊕ ... ⊕ g_n(x_n)=0$

\(x=(x_1,x_2,...x_n)\)是N态当且仅当$ g_1(x_1) ⊕ g_2(x_2) ⊕ ... ⊕ g_n(x_n)>0$


为了进一步帮助大家理解SG定理,我们思考一个问题,如何得到G的最优策略。

这是一个比较简单的问题,留作思考题供大家思考。

下面我们来看几个例题,来看看SG定理的具体应用。

例1:hdu 1524 A Chess Game

\(N (1 ≤ N ≤ 1000)\) 个点的有向无环图,有 \(M (1 ≤ M ≤ 10)\) 个棋子放在结点上,多个棋子 可以同时在一个结点上,每次玩家只能按照有向边移动一个棋子到下一个结点,没有棋子 能移动则输。求最优策略下先手赢还是输。

分析题意:这其实就是一个最基础的关于SG定理的模型。只要对于整张图的每一个点我们都能想办法求出他的SG函数值,我们就能快速得到结果。可以递推求解,也可以记忆化搜索来求。

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
ll n,m;
vector<ll>g[1010];
ll sg[1010] ;
ll dfs(ll x)
{
    if(sg[x]!=-1)return sg[x];
    
    bitset<1010>vis;
    for(auto v:g[x])
        vis[dfs(v)]=1;
    ll ans=0;
    while(vis[ans])ans++;
    return sg[x]=ans;
}
int main()
{
    cin.tie(nullptr)->sync_with_stdio(false);
    while(cin>>n)
    {
         memset(sg,-1,sizeof(sg));
        for(int i=0;i<n;i++)
        {
            g[i].clear();
            ll k;
            cin>>k;
            for(int j=1;j<=k;j++)
            {
                ll x;
                cin>>x;
                g[i].push_back(x);
            }
        }
        while(cin>>m&&m)
        {
            ll ans=0;
            for(int i=1;i<=m;i++)
            {
                ll y;
                cin>>y;
                ans^=dfs(y);
            }
            if(ans!=0)cout<<"WIN"<<"\n";
            else cout<<"LOSE"<<"\n";
        }
    }
}

例2 hdu 1536 S-Nim

给定一个集合$ S = \left\ {s_1,s_2, ...,s_k \right\ } (k ≤ 100, 1 ≤ s_i ≤ 10000)$,有 l (1 ≤ l ≤ 100) 堆石子,第 i (1 ≤ i ≤ l) 堆石子的数量为 \(h_i (0 ≤ h_i ≤ 10000)\)。每次玩家可以从一堆石子中取走 s (s ∈ S) 个,不能取则输,求最优策略下先手赢还是输。

这是一个稍微复杂一点的nim游戏,但是在我们学了SG函数后,这个问题也变成一个非常简单的模板题了,和上面的做法类似。

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
ll k,m;
ll a[10010];
ll sg[10010];
ll dfs(ll x)
{
    if(sg[x]!=-1)return sg[x];
    bitset<10010>vis;
    for(int i=1;i<=k;i++)
    {
        if(x-a[i]>=0)
        vis[dfs(x-a[i])]=1;
    }
    ll ans=0;
    while(vis[ans])ans++;
    return sg[x]=ans;
}
int main()
{
    cin.tie(nullptr)->sync_with_stdio(false);
    while(cin>>k&&k)
    {
        memset(sg,-1,sizeof(sg));
        //sg[0]=0;
        for(int i=1;i<=k;i++)
            cin>>a[i];
        cin>>m;
        while(m--)
        {
            ll l;
            cin>>l;
            ll ans=0;
            for(int i=1;i<=l;i++)
            {
                ll x;
                cin>>x;
                ans^=dfs(x);
            }
            if(ans!=0)cout<<"W";
            else cout<<"L";
        }
        cout<<"\n";
    }
    return 0;
}

例3 hdu 1729 Stone Game

有 n (1 ≤ n ≤ 50) 个盒子,第 i (1 ≤ i ≤ n) 个盒子的容量为 \(s_i (0 ≤ s_i ≤ 10^6 )\),初始有 $c_i (0 ≤ c_i ≤ s_i) $个石子。假设当前某个盒子中有 x 个石子,则玩家可以往里放 1 到 \(x ^2\) 个 石子,但放完后不能超过该盒子的容量。玩家轮流放石子,每次选择一个盒子放,不能放 的玩家输。求最优策略下先手赢还是输。

也是一个类似的问题,只是终态从取空变成了放满。解决方法比较类似。留给大家自己思考尝试

基于nim游戏的模型和变种

分裂游戏

n 堆石子,每次玩家可以从一堆中取走若干个石子或把一堆石子分成两非空的

很显然就是在nim博弈上多加了一个选项。遇到问题,如果考虑不清楚,先对每一小堆的情况打表观察

x 0 1 2 3 4 5 6 7 8
g(x) 0 1 2 4 3 5 6 8 7

有一个结论 当x形如4k+1,4k+2的数sg函数值等于它本身,x形如4k+3和4k+4时,他们的sg函数值是互相交换的结果。

如果打表的过程手算仔细,其实会发现这也是异或的性质造成的结果,在比赛中,发现结论不用着急证明,要有发现性质,大胆猜测的勇气。具体证明过程不难理解,核心就算是可以通过分裂4k+3为4k+2和1,他们的异或总能构造出它本身导致他的sg值+1.而4k+4总会面对分解不出4k+3的情况。所以会有此结论。

例1 hdu 2509 Be the Winner

有 m 个苹果排成一列,初始时分为 n (1 ≤ n ≤ 100) 段,每段不超过 100 个。每次可以从 一段中取走连续的若干个苹果,假如取走了中间的一段,则左右变为独立的两段。取走最 后一个苹果的人输。求最优策略下先手赢还是输。

分析问题,本质上还是一个nim游戏的模型,他和上面分裂游戏的最大区别在于,他如果要分段,至少要拿走1个,也就是说,所有他能到达的状态就是

\(g(x)=mex\left\{ g(l)⊕g(r)|l+r<x\right\}\)对于这个问题\(n^3\)暴力维护sg函数即可。

隐藏的nim游戏

移动棋子

有一个 1 × n 的棋盘,其中每个格子可以有多个棋子,每次选择一个棋子,将其移动到更左边的任意一个格子,两人轮流移动,不能移动则输。

对于这个问题,很显然能发现对于每个棋子,我们可以把它看作一堆石子,它左边有多少格子,就代表这堆石子有多少数量。就转换成了最经典的nim游戏的模型。

取放棋子

有 n 堆石子,排成一行,标号为 1, 2, ..., n,每次可以选择两堆 i < j,满足第 i 堆中至少有 一个石子,当前玩家从第 i 堆中取走一个石子,并把一个石子放到第 j 堆中。两人轮流操 作,不能操作的人输。

这个问题比起上面的问题,稍微复杂点,但是本质上其实仍然是nim游戏问题,用到了2个比较常见的技巧。

第一个是,把终点看作最右边即可。

第二个是,因为每次只能移动1个石子,而这又是一个公平游戏。所以所有石子都可以两两抵消,因为,每个成对的棋子总可以通过模仿最终消除。所以每个格子上的石子数量并无关系,只和每个位置的石子奇偶性有关。

SG定理+?

这个部分的内容非常多,比如SG定理加数据结构维护,又或者涉及一些树上或图上的博弈问题。比如经典的Green Hackenbush游戏等等,限于篇幅,不能展开全部介绍,大家可以在掌握sg定理后,自行尝试结合起来使用,一般这类问题,主要考察的还是对SG定理本身的理解,结合的内容只是工具。所以对SG定理和函数本身的理解才是这部分内容的重中之重,大家可以自行练习。

经典的组合游戏

Fibonacci Nim


初始时有 n 个石子,两个人轮流取石子。规则为先手第一次可以取 1 到 n − 1 个石子 之后每一次,可取的数量为 1 到上一个人取的石子数的 2 倍 不能取的人输


有了前面的很多前置知识,这个问题我们先尝试分析一下。不好从sg函数角度下手,那就还是先打表尝试手算,找找规律。这个过程并不复杂,但却是一个非常重要的能力,在正式比赛中。真正需要的是快速准确打表,思考策略和猜结论的过程。平时训练过程中。多自己尝试分析问题,

回到这个问题,其实情况并不复杂,并且很容易得到一个非常简洁的结论

结论:局面是P态当且仅当n为Fibonacci数。

实际上如果在比赛中,找到了类似的结论,就可以开始大胆验证了。不过平时训练的过程中。我们还是尽量给出严谨科学的证明,如果可行,最好给出构造性的证明。也就是说,分析清楚各种状态下的必胜策略。可以帮助思考模拟打表的过程。

那么证明这个结论之前,我们需要先引入一个定理

Zeckendorf's Theorem

每个正整数可以唯一的写成一些不相邻的 Fibonacci 数的和(其中 Fibonacci 数 {Fn} 满足\(F_1 = 1, F_2 = 2 且 F_n = F_{n−1} + F_{n−2} (n > 2)\)

也就是说,给定任何一个数,总能将其表示为一些Fibonacci数的和,

简单给出这个定理的证明:

我们先假设存在$n=F_{x_1}+F_{x_2}+...+F_{x_k} $ 且$ x_1>x_2>...>x_k$

取\(F_{x_1}\)为<=n的最大的Fibonacci数,

即\(n-F_{x_1}=F_{x_2}+...+F_{x_k}\)

所以有$ F_{x_1}\leq n \leq F_{x_1+1}=F_{x_1}+F_{x_1-1}$

于是\(n-F_{x_1} < F_{x_1-1}\)

所以取出的所有Fibonacci数都不相邻,且取法唯一。

我们只需贪心的从大向小取即可。

大家可以随便举一些数字验证这个定理。

必胜策略

知道了这个定理,我们来继续思考这个游戏的必胜策略,也即是具体如何拿来保证自己能赢。

如果n不是Fibonacci数,那么它一定可以表达为$n=F_{x_1}+F_{x_2}+...+F_{x_k} $的形式

那么先手只需要取走最小的一个Fibonacci数\(F_{x_k}\) 即可

因为根据Zeckendorf‘s Thorem,任意两个Fabonacci数不相邻,所以后手只能取一个数\(x<=2*F_{x_k}<F_{x_{k-1}}\)

先手只需要取\(F_{x_{k-1}}-x\)即可将局面返回到要求的状态。

接下来我们尝试分析n是Fibonacci数的情况.

对于\(n=F_{x}\)我们假设先手先取m个石子。

我们分情况讨论,如果\(m >= F_{x-2}\)

那么 \(n-m=F_x-m \leq F_x- F_{X-2} =F_{x-1} \leq 2*F_{x-2}\) 后手可以直接取完获胜。

如果\(m \leq F_{x-2}\)

那么相当于把n看作\(n=F_{x-1}+F_{x-2}\) 从后半部分取数字。

我们可以继续分类讨论,如果\(m >= F_{x-4}\)

\(n-m=F_x-m \leq F_{x-1}+ F_{x-2}-m \leq F_{x-1} +F_{x-3}\)

那么后手只需要取\(F_{x-2}-m\) 即可讲局面继续转换为初始状态, 一个完整的Fabonacci数。

如果继续向后讨论,\(m<=F_{x-6}\),可以得到无限细分的上述情况,直到归纳0为止。

所以如果n是Fibonacci数的情况,假设先手取m个

如果\(m >= F_{x-2}\) 后手取\(F_x-m\)

如果$m< F_{x-2} $ 继续分类 如果\(m >= F_{x-4}\) 后手取\(F_{x-2}-m\)剩下一个\(F_{x-1}\)

以此类推。

也就是说实际游戏过程中 $ n-m= F_{x_1}+F_{x_2}+...+F_{x_k} $ 后手取\(F_{x_K}\)即可。

大家可以自己手写分析这个过程,也可以互相讨论交流尝试模拟这个游戏的过程,可以帮助大家更好的理解这个游戏的策略。

思考题


初始时有 n 个石子,两个人轮流取石子。规则为先手第一次可以取 1 到 n − 1 个石子 之后每一次,可取的数量为 1 到上一个人取的石子数, 不能取的人输

chomp游戏


一开始有 n × m 块饼干,排列成一个 n 行 m 列的矩形,其中位于 (1, 1) (左上角)的饼干 是有毒的。两个人轮流进行游戏,每次可以取当前还没被取的一块饼干 (x, y),并把所有 (i, j) 满足 i ≥ x 或 j ≥ y 的饼干都吃掉。吃到有毒的饼干就会死。请问先手死还是后手死。


按照惯例,我们可以打表尝试分析问题,容易得到结论。

结论:所有(n,m)满足max(n,m)的矩形均为N态.

结论比较显然,如果暴力考虑所有的情况,需要枚举各种各样的轮廓线来解决问题,复杂度是指数级别,

我们尝试思考结论的正确性。上面的大多数证明,我们往往给出一个构造性的证明手段,可以帮我们顺便了解必胜策略。

但是对于这个游戏,我们不容易给出构造性的方案,证明如下。

如果先手取(n,m)时,后手存在必胜策略(a,b)那么先手一定可以直接取(a,b)来获胜。

必胜策略

考虑上面的证明,只能说明结论的正确性,不能给我们正确的策略启发这个问题,所以如果想知道必胜策略,需要暴力搜索或者枚举轮廓线,插头dp来解决这个问题,但是复杂度都是指数级别复杂度,只能处理小范围的问题

思考题


给定 n, m (1 ≤ n, m ≤ 8),求初始状态为 n × m 矩形的 Chomp 游戏先手第一步的所有方案 使得他必胜。


此外 ,他还可以扩展到三维上进行,也可以在有限偏序集上做Chomp游戏

Wythoff游戏


有两堆石子,石子数分别为 n, m。两个玩家轮流进行,轮到一方的时候,该玩家可以做 从其中一堆石子中取走 x ≥ 1 个( x 不能超过该堆的石子数),或从两堆石子中同时取走 x ≥ 1 个( x 不能超过任意一堆的石子数)。 如果轮到某方的时候,玩家无法操作,则该玩家输


先尝试抽象化问题,问题可以转换为,在一个棋盘上,每个点可以选择向左移动任意位,或想向上移动任意位置,或者斜着移动任意位。

接下来开始打表分析。

image

可以大概得到这样一张表描绘所有P态的位置

i 0 1 2 3 4 5 6
\(a_i\) 0 1 3 4 6 8 9
\(b_i\) 0 2 5 7 10 13 15

其中PN态关于n,m是对称的,

可能不太好发现规律。

这个直接给出大家结论。

按上述过程得到的序列 \(a_i ,b_i\),满足 $a_i = \lfloor φi \rfloor $ \(b_i = \lfloor φ^2i \rfloor\) 其中 $φ = 1+√ 5 $ 是黄金分割比。

具体的证明过程需要引入Beatty序列和Rayleigh定理,篇幅较长,这里不展开说明

感兴趣的可以参考捡石子游戏、 Wythoff 数表和一切的 Fibonacci 数列 | Matrix67: The Aha Moments来学习。

二分图博弈


给出一个二分图和一个起始点H,两人轮流操作,每次只能选择一个与当且点相邻的之前没走过的点,走到该点,谁先不能走谁输


这个问题需要一定的图论相关知识,不太容易观察得到。所以这里我们直接给出结论

结论 :如果二分图的最大匹配一定包含起始点H,则先手必胜,否则先手必败。

我们给出一个比较简单的证明

首先考虑H一定在最大匹配的情况,先手只需要选任意最大匹配中的边,走到二分图的另一部分。此时后手如果不能移动,他就直接失败,如果它可以移动,他一定只能移动到最大匹配中的边。因为如果它可以走到不在最大匹配中的边,那么H和这个边的终点交换不会改变最大匹配的值,违反了假设,所有最大匹配一定包含起始点H,不成立。所以只能在图中最大匹配中来回走,先手始终比后手多1条边可以走,所以这种情况,先手必胜。

接下来考虑H不在最大匹配中的情况,这时候H只能走到一个最大匹配中的点,从而陷入上述后手面临的情况,而H如果能走到一个目前不在最大匹配中的点,那么这两点都不在最大匹配中,完全可以加入匹配中,匹配数会+1,不满足假设的目前是最大匹配,故这种情况也不成立,所以结论得证。

因为一个二分图可能有很多最大匹配,

实际应用中。我们只需要跑两遍最大匹配即可。

如果原图去除H点的最大匹配m1等于原图的最大匹配m2 ,说明存在不包含m的最大匹配,此时后手必胜。反之如果m2=m1+1 说明所有最大匹配都必须包含H,先手必胜.这个问题可以通过跑两遍二分图的最大匹配来解决,匈牙利算法可以O(nm)完成,dinic可以做到O(sqrt(n)m)。

一个思考题


2020 ccpc 长春 H


有一个锁,由 m 个环构成,每个环有数字 0 到 9,组成一个 m 位密码。Alice 和 Bob 在玩 游戏,每次可以把锁的某个环旋转一位(模 10 意义下加一或减一),不能移到之前已经移 到过的密码,也不能移到 n 个给定的密码,不能移动的人输。Alice 先手,给定初始密码, 问谁赢。(m ≤ 5)


问题核心还是一个图论建模问题,大家可以自行尝试解决。

看到这里,大家已经了解组合游戏的大部分经典模型,但是组合游戏仍然有各种各样的不同考法,也还有nim积这样的经典问题本文暂时没有涉及,限于篇幅,nim积相关和不平等的组合游戏这里就先不展开讲述了,今天所涉及的内容也已经包含了非常多的内容,大家可以先多看看这些经典问题,在尝试去做一些足有游戏的各种变形,nim积相关和不平等的组合游戏后续也会更新到这篇文档中,也欢迎大家自行阅读学习。下面给出一些经典博弈问题的题单。

题目编号 题目名 所在oj 难度评级
1 Brave Game HDU 1846
2 kiki's game HDU 2147
3 Public Sale HDU 2149
4 取石子游戏 HDU 2516
5 Bash游戏 51Nod 1066
6 Bash游戏V2 51Nod 1067
7 Bash游戏V3 51Nod 1068 ⭐⭐
8 Bash游戏V4 51Nod 1070 ⭐⭐
9 Nim游戏 51Nod 1069
10 Nim游戏V2 51Nod 1071 ⭐⭐
11 Good Luck in CET-4 Everybody! HDU 1847 ⭐⭐
12 Rake it in https://nanti.jisuanke.com/t/A1538 ⭐⭐⭐
13 Digital Deletions HDU - 1404 ⭐⭐⭐
14 Palindrome Game (hard version) cf1527B2 ⭐⭐⭐
15 小牛再战 F-小牛再战_2018年牛客多校算法寒假训练营练习比赛(第三场) (nowcoder.com) ⭐⭐⭐
16 Northcott Game HDU - 1730 ⭐⭐⭐
17 Triangle Game HDU 7216 ⭐⭐⭐
18 Alice and Bob HDU 7149 ⭐⭐⭐
19 Fibonacci again and again HDU 1848 ⭐⭐⭐
20 THUNDER Bluff HDU 7225 ⭐⭐⭐⭐⭐⭐
21 Game HDU - 4023 ⭐⭐⭐⭐
22 A Chess Game HDU 1524 ⭐⭐⭐
23 S-nim HDU 1536 ⭐⭐⭐
24 Stone Game HDU 1729 ⭐⭐⭐
25 Be the Winner HDU 2509 ⭐⭐⭐
26 [HNOI2007]分裂游戏 P3185 ⭐⭐⭐⭐⭐
27 A tree game HDU 3094 ⭐⭐⭐⭐⭐
28 Poachers cf1585G ⭐⭐⭐⭐⭐
29 Cutting Game POJ - 2311 ⭐⭐⭐
30 NIM POJ - 2068 ⭐⭐
31 Crosses and Crosses POJ - 3537 ⭐⭐⭐
32 魔法珠 ACwing237 ⭐⭐⭐
33 Hakase and Nano HDU - 6266 ⭐⭐⭐
34 Fibonacci NIM HDU - 2188
35 Fibonacci NIM HDU - 2516 ⭐⭐
36 待更
37
38
39

标签:状态,Theory,可以,石子,玩家,Game,SG,游戏
来源: https://www.cnblogs.com/tscjj/p/16597435.html