博弈论学习笔记

借鉴/感谢:自为风月马前卒Candy,贾志豪《组合游戏略述——浅谈SG游戏的若干拓展及变形》(发现前两位竟然都是我省神仙

由于不写博客就容易颓废(其实写了也还是颓废 所以开一个博客记录一下 这样有点动力...

应该先是看贾志豪的《组合游戏略述——浅谈SG游戏的若干拓展及变形》然后后面会接一些题 这里会放一些笔记

 

零、前置

我们考虑的是一类组合游戏问题,前提是两人都以最优策略进行,不存在平局现象且在有限步内结束。

任意确定状态可以做出的策略集合只与状态有关,与游戏者无关

一、定义

游戏图:把状态抽象成点,转移抽象成边的DAG

SG函数:是对游戏图每一个点的一个评估函数。定义式为SG(x)=mex{SG(y)|(x,y)∈E}。

根据拓扑序转移SG 先手必胜的条件SG不等于0。

两个显然的性质:

1)对于任意的局面,如果它的SG值为0,那么它的后继一定没有0。

2)对于任意的局面,如果它的SG值为0,那么它至少有一个后继SG值等于0。

Nim模型:取石子游戏 $f_i$表示一堆石子的个数,每次选择一个$f_i$减少一个$x$($x<=f_i$)。

$f_{tot} = f_1 \oplus f_2 \oplus ... \oplus f_n$ 先手必胜当且仅当f_{tot}不等于0。

我们看到SG函数和nim游戏的定义十分相似,考虑两者之间的关系。

可以发现,每个简单SG游戏可以完全等效成一堆数目为SG(x)的石子。

归纳证明:

1.最终状态SG=0

2.对于任意必胜态(异或和不为0),必定存在一个后继必败状态(异或和为0)

即一定有一种取法使Nim和再变成0(具体考虑类似于线性基 一位一位往下取即可)

3.对于任意必败态(异或和为0),必定不存在任意一个后继必败状态(异或和为0)

显然,需要取走非零颗石子,异或和必定改变。

引入游戏的和概念

考虑任意多个同时进行的SG-组合游戏,这些SG-组合游戏的和是这样一个SG-组合游戏,在它进行的过程中,游戏者可以任意挑选其中的一个单一游戏进行决策,最终,没有办法进行决策的人输。

发现SG和Nim的定义类似 我们就可以把游戏的和转换为Nim模型处理,即$SG(x)=SG(x_1) \oplus SG(x_2) \oplus ... \oplus SG(x_n)$。

注意:游戏的和与SG函数的递推不同,前者是多个同时进行的SG游戏选择其中一个进行决策,最终需要每一个都决策,后者只需要决策一步就达到后继状态,而没有多次决策的过程。比如说前者就是多个游戏图上选择一个进行游戏,后者就是单独的一个游戏图上决策。需要区分两个概念。

二、模型拓展——Anti-SG游戏和SJ定理

走了最后一步的人输,那么怎么判断先手胜负呢?

我们回到最开始的Nim游戏 类似的定义Anti-Nim,规定取走最后一枚石子的人输。

我们一步一步推理出结论。游戏分为两种情况。

1)每一堆石子都为1。先手必胜当且仅当N是偶数。相当于SG=0。

2)

其它情况。

{

    1)当SG为0时。

    至少还有两堆石子的数目>1,先手决策完至少还有一堆石子数目>1,且SG一定不等于0,带入到第二种情况。最后可以得到结论一定可以走到第二种情况的前半部分,所以说先手必败。

    2)

    若至少只有一堆石子的数目>1,先手总可以将石子变成奇数堆1。那么先手必胜。若至少有两堆石子的数目>1,我们一定可以把SG变为0,带入回第一种情况SG=0。而SG为0一定最终带入回前者情况,所以先手必胜。

}

结论

先手必胜当且仅当

1)所有堆石子数都为1,且SG=0。

2)有至少一堆石子数量大于1,且SG!=0。

 

我们有了最初的模型,可以如下定义Anti-SG游戏

Anti-SG游戏规定,决策集合为空的游戏者胜利。

 

也可以得到SJ定理

先手必胜当且仅当:

1)$SG_{tot}$不为0,且存在某个单一的SG游戏其SG值大于1

2)$SG_{tot}$等于0,且不存在单一的SG游戏其SG值等于1

证明在此略去。类似于Anti-Nim的证明。

 

三、模型拓展——MultiSG

MultiSG就是类似于前面介绍的游戏的和的概率。

举个例子:MultiNim

还是很多石子堆,但每次除了可以选择拿走石子,还可以选择把一堆石子分成n堆石子(n>=2)

发现SG(3)的后继转移就有{0}{1}{2}{1,2}这4种,而{1,2}这种的SG就相当于SG(1)^SG(2)(游戏的和)。然后SG(3)的转移就是这四种的mex啦。

MultiNim游戏具有结论性质如下:

(x%4==0) SG(x)=x-1

(x%4==1/2) SG(x)=x

(x%4==3) SG(x)=x+1

得,全网没找到靠谱证明。先咕着吧。

现在考虑MultiSG

类似于MultiNim,MultiSG后继的状态有分裂的那些取mex,而分裂出的局面的SG是xor。

 

四、模型拓展——EverySG

如果每个棋子都需要移动,那么我们就来到了EverySG

对于一个先手必胜的局面,先手一定要尽可能让这个游戏玩的时间久一点,让它成为最后一次游戏,来保证先手必胜。

而他的对手则希望这个游戏尽快结束,不至于让输的局面是最后一次游戏。

根据我们上面的推论我们用一个$step(x)$来作为评估函数 它具体的转移如下

$step(x) = 0 $ x为终止状态

$step(x) = max(step(y))+1$ sg(x)>0 sg(y)=0

$step(x) = min(step(y))+1$ sg(x)=0

定理:对于EverySG游戏,先手必胜当且仅当单一游戏中最大的step为奇数

什么?你要证明?好像很显然啊...你可以选择分类讨论一下吧(其实是step已经是一个确定性函数了

 

五、实例——翻硬币游戏

1-N共N枚硬币,每次翻可以选择翻连续几个,但要保证最右的那一枚是正面翻到反面。

结论:局面的SG值是局面中每个正面向上的硬币单一存在(只有它正面)时的SG值的异或。

具体证明其实就可以考虑和Nim完全等价,每次正到反的位置都是向左移。

 

六、实例——无向图删边游戏

1.树的删边游戏

这是一道常见题 也有相应的结论

结论:叶子节点的SG为0,其余节点的SG为它的儿子节点SG+1后的异或。

理解:与+1对应的是砍掉整个子树,而每个子树是独立的,因此是异或。

 

2.简化的无向图删边

我们现在考虑这样一个无向图,它现在是一棵树,有一些边,保证形成的环不存在公共边,且只与原树有一个交点。

我们发现,这个图上的所有环都是单独连出去又连回来的。

结论

对于奇环来说,去掉任意一条边,剩下的两段同奇偶,异或后不会出现奇数,所以它的SG值为1。

对于偶环来说,去掉任意一条边,剩下的两段一定不相同,异或后不会出现0,它的SG值为0。

把奇环变成长度为1的链,偶环变成一个点,我们就回到了之前的问题。

 

3.无向图删边

结论:对于任意的奇环都可以缩成一个新点+一个新边,偶环缩成新点,原来与其相连的边全部连到新点上。直到变成一棵树就是我们之前的做法了。

证明很神仙,我也不会,脑补一下还是很科学的(逃。

 

七、实例——阶梯Nim

阶梯Nim:每次可以选择一堆石子把其中的x枚石子移到左边的一堆(从第一堆往左移相当于取走)。不可操作者输。

结论:等价于所有奇数层石子的Nim。

证明很简单,考虑每次从奇数层往下移,后手一定可以复制先手的操作使Nim继续。

 

理论到这里就结束了,我们来看题吧。

八、习题

解法和代码一起放在ViewCode里了,读者可以先自行思考在看。

POJ2848

这真的是想不到啊。。。
自己的思路是考虑第一次拿走了以后就变成序列操作了,然后一顿SG推理得出结论,我不会做。
实际上解法很简单,除了n=1/n=2是先手必胜(一次取走),其余都是后手必胜。因为考虑当n=偶数的时候,后手只需要中心对称的模仿先手的操作即可。n=奇数的时候,如果先手取了1,后手只需要对称取走2就变回第一种情况;先手取了2,后手对称取1即可。好神仙啊。。
//Love and Freedom.
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
#define inf 20021225
using namespace std;
int read()
{
    int s=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return f*s;
}
int n;
int main()
{
    while(n=read())
        if(n==1||n==2)    printf("Alice\n");
        else    printf("Bob\n");
    return 0;
}
View Code

BZOJ2463

鉴于第一题的机智经验和之前好像看到过这个题。。。
顺利切掉了QAQ
具体证明可以考虑1*2骨牌覆盖,先手位于骨牌开始位置,它只需要移动到骨牌的另一位置,而后手需要寻找新的骨牌。对于偶数一定存在合法的骨牌覆盖,但是奇数的话相当于先手陷入了寻找新骨牌的困境,所以判断奇偶就可以了。
//Love and Freedom.
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
#define inf 20021225
using namespace std;
int read()
{
    int s=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return f*s;
}
int main()
{
    while(int n=read())
        if(n&1)    printf("Bob\n");
        else    printf("Alice\n");
    return 0;
}
View Code

BZOJ3895

我太难了 尝试推结论未遂。。。
打开百度:记忆化搜索???
大概是这个样子,我们发现一堆里头如果只有一个石子,它比较特殊,因为它取走了其实相当于进行了两个操作。。。所以我们设f[a][b]表示有a个1石子堆,其余石子需要操作b次的答案,然后这样的话是O(n*n*m)跑记忆化搜索刚刚好。
//Love and Freedom.
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
#define inf 20021225
#define N 50100
using namespace std;
int read()
{
    int s=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return f*s;
}
int f[51][N];
int dfs(int a,int b)
{
    if(a==0)    return b&1;
    if(b==1)    return dfs(a+1,0);
    if(~f[a][b])    return f[a][b];
    if(a&&!dfs(a-1,b))    return f[a][b]=1;
    if(a&&b&&!dfs(a-1,b+1))    return f[a][b]=1;
    if(b&&!dfs(a,b-1))    return f[a][b]=1;
    if(a>=2&&!dfs(a-2,b+2+(b?1:0)))    return f[a][b]=1;
    return f[a][b]=0;
}
int main()
{
    memset(f,-1,sizeof(f)); int T=read(),n,x;
    while(T--)
    {
        n=read(); int cnt=0,val=-1;
        for(int i=1;i<=n;i++)
        {
            x=read();
            if(x==1)    cnt++;
            else    val+=x+1;
        }
        if(val==-1) val=0;
        puts(dfs(cnt,val)?"YES":"NO");
    }
    return 0;
}
View Code

POJ1740

感觉智商疯狂被碾压...
考虑有两堆相等的,后手只需要复制先手的操作就行了。
推广到每堆都有与之相等的(也就是一对一对的),后手还是复制先手的操作就行了。
接下来考虑别的情况,我们发现先手只需要把每一组都补齐,就可以使后手陷入必败的局面。为什么一定能补齐呢?我们考虑将石子排序。
对于n为奇数,显然第n组一定是大于其它相邻一对的差的和的。
对于n为偶数,第n个和第1个配对,剩下的也一定大于相邻一对的差的和的。
//Love and Freedom.
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
#define inf 20021225
using namespace std;
int read()
{
    int s=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return f*s;
}
int buc[101];
int main()
{
    while(int n=read())
    {
        int pr=0;
        for(int i=1;i<=n;i++)
        {
            int x=read();
            if(buc[x]) buc[x]--,pr--;
            else    buc[x]++,pr++;
        }
        if(!pr)    printf("0\n");
        else    printf("1\n"),memset(buc,0,sizeof(buc));
    }
    return 0;
}
View Code

BZOJ1982

这个题怎么和上个题一样啊...
//Love and Freedom.
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
#define inf 20021225
using namespace std;
int read()
{
    int s=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return f*s;
}
int a[100001],n;
int main()
{
    while(~scanf("%d",&n))
    {
        int pr=0;
        for(int i=1;i<=n;i++)    a[i]=read();
        sort(a+1,a+n+1);
        for(int i=1;i<=n;i+=2) if(a[i]!=a[i+1]){pr=1; break;}
        if(!(n&1) && !pr)    printf("second player\n");
        else    printf("first player\n");
    }
    return 0;
}
View Code

POJ2505

我没有脑子...
首先[1,9]是Stan胜,[10,18]是Ollie胜。
Stan要越接近N取胜,而Ollie要尽量拖住Stan,所以每次的区间就是这样的:[2*9*...*2*9+1,2*9*...*2*9*9]Stan胜,其余Ollie胜。
//Love and Freedom.
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
#define inf 20021225
#define db double
using namespace std;
int read()
{
    int s=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return f*s;
}
int main()
{
    db n;
    while(scanf("%lf",&n)!=EOF)
    {
        while(n>18)    n/=18;
        if(n<=9)    printf("Stan ");
        else    printf("Ollie ");
        printf("wins.\n");
    }
    return 0;
}
View Code

POJ2975

这个还是会做的嘤嘤嘤。
我们考虑从一个Ai的异或中改变一个数使得新的异或=0
只需要判断一下大小关系就可以啦w
//Love and Freedom.
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
#define inf 20021225
#define N 1001
using namespace std;
int read()
{
    int s=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return f*s;
}
int a[N];
int main()
{
    while(int n=read())
    {
        int s=0;
        for(int i=1;i<=n;i++)    a[i]=read(),s^=a[i];
        int ans=0;
        for(int i=1;i<=n;i++) if((s^a[i])<a[i])
            ans++;
        printf("%d\n",ans);
    }
    return 0;
}
View Code

BZOJ1299

发现先手一旦可以找到一个Nim=0的,后手就没了。所以说只需要跑一遍就行了。
//Love and Freedom.
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
#define inf 20021225
#define N 15
#define lowbit(x) (x&-x)
using namespace std;
int read()
{
    int s=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return f*s;
}
int n,f[1<<N];
int main()
{
    int T=10;
    while(T--)
    {
        n=read();
        for(int i=0;i<n;i++)    f[1<<i]=read();
        int i,top=1<<n;
        for(i=1;i<top;i++)
        {
            f[i]=f[i^lowbit(i)]^f[lowbit(i)];
            if(!f[i])    break;
        }
        puts(i==top?"YES":"NO");
    }
    return 0;
}
View Code

POJ2425

SG函数裸题。应该看懂博客了的都能会= =
//Love and Freedom.
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
#define inf 20021225
#define N 1001
using namespace std;
int read()
{
    int s=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return f*s;
}
struct edge{int to,lt;}e[N*N];
int in[N],cnt,f[N],stk[N],n; bool vis[N];
void add(int x,int y)
{
    e[++cnt].to=y; e[cnt].lt=in[x]; in[x]=cnt;
}
int mex(int *a,int n)
{
    sort(a+1,a+n+1); if(a[1])    return 0;
    for(int i=2;i<=n;i++)    if(a[i]>a[i-1] && a[i]!=a[i-1]+1)
        return a[i-1]+1; return a[n]+1;
}
void dfs(int x)
{
    if(vis[x])    return; vis[x]=1; int top=0;
    for(int i=in[x];i;i=e[i].lt)    dfs(e[i].to);
    for(int i=in[x];i;i=e[i].lt)    stk[++top]=f[e[i].to];
    f[x]=mex(stk,top); if(!in[x])    f[x]=0;
}
int main()
{
    while(~scanf("%d",&n))
    {
        memset(vis,0,sizeof(vis));
        memset(in,0,sizeof(in));
        cnt=0; int x,ans;
        for(int i=0;i<n;i++)
        {
            int d=read();
            while(d--) x=read(),add(i,x);
        }
        while(int m=read())
        {
            ans=0;
            while(m--)
            {
                x=read(); dfs(x);
                ans^=f[x];
            }
            puts(ans?"WIN":"LOSE");
        }
    }
    return 0;
}
View Code

POJ2960

还是这种套路一点的比较适合我...
//Love and Freedom.
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#define ll long long
#define inf 20021225
#define N 10010
#define vec vector<int>
#define pb push_back
using namespace std;
int read()
{
    int s=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return f*s;
}
int f[N],buc[N],k,s[N]; vec to[N];
int mex(vec a)
{
    if(!a.size())    return 0; int ans=a.size();
    for(int i=0;i<a.size();i++)    buc[a[i]]++;
    for(int i=0;i<a.size();i++)    if(!buc[i]){ans=i;break;}
    for(int i=0;i<a.size();i++)    buc[a[i]]--; return ans;
}
int dfs(int x)
{
    if(~f[x])    return f[x]; to[x].clear();
    for(int i=1;i<=k;i++)    if(x-s[i]>=0)    to[x].pb(dfs(x-s[i]));
    return f[x]=mex(to[x]);
}
int main()
{
    while(k=read())
    {
        memset(f,-1,sizeof(f));
        for(int i=1;i<=k;i++)    s[i]=read();
        sort(s+1,s+k+1); int m=read(),ans,w;
        while(m--)
        {
            int x=read(); ans=0;
            while(x--)    w=read(),ans^=dfs(w);
            putchar(ans?'W':'L');
        }
        printf("\n");
    }
    return 0;
}
View Code

 BZOJ1874

喵的我会做这个题TAT 题意杀我
它的意思是每次取的石子数必须是给出的其中一个,不是要按顺序取...
所以做法就是暴力SG啦...
//Love and Freedom.
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
#define inf 20021225
#define N 1001
using namespace std;
int read()
{
    int s=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return f*s;
}
int f[N],buc[12],st[12],b[12],n,m;
int mex(int a[],int top)
{
    for(int i=1;i<=top;i++)    buc[a[i]]++;
    int ans=12;
    for(int i=0;i<12;i++)    if(!buc[i])
    {
        ans=i; break;
    }
    for(int i=1;i<=top;i++)    buc[a[i]]--;
    return ans;
}
int get(int x)
{
    if(~f[x])    return f[x];
    int to[11],top=0;
    for(int i=1;x>=b[i]&&i<=m;i++)
        to[++top]=get(x-b[i]);
    return f[x]=mex(to,top);
}
int main()
{
    n=read();
    for(int i=1;i<=n;i++)    st[i]=read();
    m=read();
    for(int i=1;i<=m;i++)    b[i]=read();
    memset(f,-1,sizeof(f));
    int ans=0;
    for(int i=1;i<=n;i++)    ans^=get(st[i]);
    if(!ans) return puts("NO"),0;
    for(int i=1;i<=n;i++)    for(int j=1;b[j]<=st[i]&&j<=m;j++)
    {
        if((ans^get(st[i])^get(st[i]-b[j]))==0)
        {
            puts("YES"); printf("%d %d\n",i,b[j]);    return 0;
        }
    }
    return 0;
}
View Code

BZOJ1115

发现要求相邻的大小关系,根据常见套路我们可以通过差分来做。发现差分了以后就变成了倒着的阶梯Nim。
//Love and Freedom.
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
#define inf 20021225
#define N 1001
using namespace std;
int read()
{
    int s=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return f*s;
}
int a[N],d[N];
int main()
{
    int T=read();
    while(T--)
    {
        int n=read(); int ans=0;
        for(int i=1;i<=n;i++)    a[i]=read(),d[i]=a[i]-a[i-1];
        for(int i=n;i>0;i-=2)    ans^=d[i];
        puts(ans?"TAK":"NIE");
    }
    return 0;
}
View Code

 HDU5996

这个我会!直接把结论搬到树上就好啦。
//Love and Freedom.
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
#define inf 20021225
#define N 100010
using namespace std;
int read()
{
    int s=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return f*s;
}
int dep[N],fa[N],a[N];
int main()
{
    int T=read();
    while(T--)
    {
        int n=read(),ans=0;
        for(int i=1;i<n;i++)    fa[i]=read(),dep[i]=dep[fa[i]]+1;
        for(int i=0;i<n;i++)    a[i]=read(),ans^=(dep[i]&1)?a[i]:0;
        puts(ans?"win":"lose");
    }
    return 0;
}
View Code

POJ1704

这个我也会!差分就可以了qwq 倒着的阶梯nim
最后注意给出的不一定有序QAQ!
//Love and Freedom.
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define ll long long
#define inf 20021225
#define N 1001
using namespace std;
int read()
{
    int s=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1; ch=getchar();}
    while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
    return f*s;
}
int d[N],p[N],n;
int main()
{
    int T=read();
    while(T--)
    {
        int n=read(),ans=0;
        for(int i=1;i<=n;i++)    p[i]=read();
        sort(p+1,p+n+1);
        for(int i=1;i<=n;i++)    d[i]=p[i]-p[i-1]-1;
        for(int i=n;i>0;i-=2)    ans^=d[i];
        if(ans)    printf("Georgia will win\n");
        else    printf("Bob will win\n");
    }
    return 0;
}
View Code

 

做博弈论让我日常怀疑我有没有脑子这个问题...

posted @ 2019-09-11 10:30  寒雨微凝  阅读(500)  评论(1编辑  收藏  举报