博弈论题集

本文介绍了几个博弈论的典型问题,包括Tokitsukaze, CSL和Stone Game的解题策略,1-2-K Game的最优玩法,以及在有向图环境下的博弈策略。通过解析游戏规则和构建解决方案,揭示了博弈论中的关键思考方式和胜负条件。" 127001028,9262324,虚拟机靶机设置与渗透提权实战,"['安全', '渗透测试', '网络扫描', '系统漏洞', '权限提升']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

D. Tokitsukaze, CSL and Stone Game

Tokitsukaze, CSL and Stone Game

题意:

有n堆石头,每次只能选择一堆取其一个

如果取不了石头,呢么就输了

如果取完后出现两堆一样多,呢么就输了

解析:

有三种特殊情况要特判

1.一开始就出现两个0

2.出现大于1次,两个一样的,或者超过2次一样的

3.a[i]+1==a[i+1],a[i+1]==a[i+2]

其他的都可以化为:a[i]=min(a[i],a[i-1]+1);

sum+=a[i]-min(a[i],a[i-1]+1);

判断sum的奇偶,为奇sjfnb,为偶sjfnb

#include<bits/stdc++.h>
#define ll long long
#define MAXN 100005
using namespace std;
int a[MAXN];

int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    sort(a+1,a+1+n);
    int num=0;
    for(int i=1;i<=n-1;i++)
    {
        if(a[i]==a[i+1])
        {
            num++;
            if(num>=2||a[i]==0)//相等的>2获得2对的的大于2,直接输
            {                  //开始就两个0,直接输//
                printf("cslnb\n");
                return 0;
            }
        }
    }
    for(int i=1;i<=n-2;i++)
    {
        if(a[i]+1==a[i+1]&&a[i+1]==a[i+2])//前面一个顶住后面两个,怎么变都是输
        {
            printf("cslnb\n");
            return 0;
        }
    }
    ll sum=0;
    for(int i=1;i<=n;i++)//直接求从0开始的变化次数,偶数的cslnb
        sum+=a[i]-(i-1);
    printf("%s\n",sum%2==0?"cslnb":"sjfnb");
    return 0;
}

D - 1-2-K Game

题意:

有n个石子,每次取1个,2个,k个,谁不能取,谁输

解析:

我们用sg函数打表,然后找规律

ac:

#include<bits/stdc++.h>
#define N 2
#define MAXN 1005
using namespace std;

//f[N]:可改变当前状态的方式,N为方式的种类,f[N]要在getSG之前先预处理
//SG[]:0~n的SG函数值
//S[]:为x后继状态的集合
int f[N],SG[MAXN],S[MAXN];

void  getSG(int n)
{
    int i,j;
    int sum=0;
    memset(SG,0,sizeof(SG));
    //因为SG[0]始终等于0,所以i从1开始
    for(i = 1; i <= n; i++){
        //每一次都要将上一状态 的 后继集合 重置
        memset(S,0,sizeof(S));
        for(j = 0; f[j] <= i && j <= N; j++)
            S[SG[i-f[j]]] = 1;  //将后继状态的SG函数值进行标记
        for(j = 0;; j++) if(!S[j]){   //查询当前后继状态SG值中最小的非零值
            sum++;
            SG[i] = j;
            break;
        }
    }
}

void solve(int n,int k)
{
    if(k%3!=0)
    {
        if(n%3==0)
            printf("Bob\n");
        else
            printf("Alice\n");
    }
    else{
        if(k==3)
        {
            if(n%4==0)
                printf("Bob\n");
            else
                printf("Alice\n");
        }
        else{
            n%=(k+1);
            if(n!=k&&n%3==0)
                printf("Bob\n");
            else
                printf("Alice\n");
        }
    }
}

int main()
{
    int t,n,k;
    cin>>t;
    while(t--)
    {
        cin>>n>>k;
        solve(n,k);//getSG(n),判断SG[n],这里n很大,我们要打表找规律
    }
    return 0;
}

https://acm.ecnu.edu.cn/contest/196/problem/A/

Cuber QQ 和 Little Fang 两人会按照游戏规则轮流写 { 1,2,⋯,N } ( N 是一个正整数)中的一个数。

游戏的规则是这样的,若一个人写下数 i , 则另一个人只能写 i+1 或 2i ( i,i+1,2i 均不超过 N )。两个人中,谁先写到 N 这个数字,谁就能获胜。

当然 Cuber QQ 为了表现自己的绅士,他让 Little Fang 先写, Little Fang 的开场是单调而固定的,他一定会写数字 1 。

由于表演需要,两个人一共要玩 T 局游戏。每局游戏都会给定提前正整数 N ,当然 Cuber QQ 和 Little Fang 的聪明程度是毋庸置疑的,所以他们都会按照最优的策略进行游戏。

你能预言他们两个人游戏的结局吗?

解析:

有向图博弈论,数据范围非常大,我们先打表

打表:

#include<bits/stdc++.h>
#define MAXN 1005
using namespace std;
int to[MAXN<<1],nxt[MAXN<<1],head[MAXN<<1];

int tot=0;
int sg[MAXN<<1];
void init()
{
    tot=0;
    memset(head,0,sizeof(head));
    memset(sg,-1,sizeof(sg));
}

void add(int u,int v)
{
    to[++tot]=v;
    nxt[tot]=head[u];
    head[u]=tot;
}

int getsg(int x)
{
    if(sg[x]!=-1) return sg[x];//非常重要的剪枝
    int vis[MAXN];
    memset(vis,0,sizeof(vis));
    for(int i=head[x];i;i=nxt[i])
    {
        int v=to[i];
        sg[v]=getsg(v);
        vis[sg[v]]=1;
    }
    for(int i=0;;i++)
        if(vis[i]==0)
            return i;
}

int main()
{
    for(int j=1;j<=1000;j++)
    {
        init();
        add(0,1);
        for(int i=1;i<j;i++)//构图
        {
            add(i,i+1);
            if(i*2!=i+1&&i*2<=j)
                add(i,i*2);
        }
        if(getsg(0)==0)//从0出发
            printf("%d\n",j);
    }
    return 0;
}

发现先手必败的为奇数位二进制位不能有1

ac:

#include<bits/stdc++.h>
#define ll long long
using namespace std;


int get2(ll x)
{
    int v=0,a[100];
    while(x)
    {
        a[++v]=x%2;
        x=x/2;
    }
    for(int i=1;i<=v;i++)
    {
        if(i%2==1)
        {
            if(a[i]==1)
                return 0;
        }
    }
    return 1;
}

int main()
{
    ll n;
    int t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lld",&n);
        int k=get2(n);
        if(k==0)
            printf("Little Fang Win\n");
        else
            printf("Cuber QQ Win\n");
    }
    return 0;
}

https://vjudge.net/problem/HDU-1524

题意:

给定一个有向图,图中的点上有若干个棋子,两个人一次移动一个棋子,棋子只能向有向边的方向移动,谁无法移动谁输

有向图sg博弈,起点多个,一个nim游戏,求各个起点出发的sg[]的异或和

如果为0,输,反之赢

#include<bits/stdc++.h>
#define MAXN 1005
using namespace std;
int to[MAXN<<1],nxt[MAXN<<1],head[MAXN<<1];

int tot=0;
int sg[MAXN<<1];
void init()
{
    tot=0;
    memset(head,-1,sizeof(head));
    memset(sg,-1,sizeof(sg));
}

void add(int u,int v)
{
    to[++tot]=v;
    nxt[tot]=head[u];
    head[u]=tot;
}

int getsg(int x)
{
    if(sg[x]!=-1) return sg[x];//非常重要的剪枝
    int vis[MAXN];
    memset(vis,0,sizeof(vis));
    for(int i=head[x];~i;i=nxt[i])
    {
        int v=to[i];
        sg[v]=getsg(v);
        vis[sg[v]]=1;
    }
    for(int i=0;;i++)
        if(vis[i]==0)
            return i;
}

int main()
{
    int n,m,x,q;
    while(scanf("%d",&n)!=EOF)
    {
        init();
        for(int i=0;i<n;i++)
        {
            scanf("%d",&m);
            for(int j=1;j<=m;j++)
                scanf("%d",&x),add(i,x);
        }
        while(scanf("%d",&q)&&q)
        {
            int ans=0;
            while(q--)
            {
                scanf("%d",&x);
                ans^=getsg(x);
            }
            printf("%s\n",ans==0?"LOSE":"WIN");
        }
    }
    return 0;
}

https://www.luogu.org/problem/P2197

解析:

// 一种通俗易懂的方法证明nim游戏
//  如果堆数为1,那么傻子都能知道先手一定会赢......
//  如果堆数为2,那么相信大家都能自己yy出做法
// 如果你是先手,那么,如果两堆石子的个数不一样,恭喜,你赢了
// 你只要先把其中石子多的取到和石子少的一样,那么后面,对手在一堆中取多少个,你就在另一堆中取相同的个数,这很easy吧
// 当然,两堆石子如果个数一样,那么先手就输了。 
// 现在,给你的石子有多少堆不知道,这应该怎么做呢?
// 我们又回到两堆石子的本质
// 其实 ,对于两堆石子来说,先手的赢和输,是依据这两堆石子的个数是否相等来判断的。
//  更深层次来说的话,两堆石子相同,即个数一样,那么,这不就是异或为0吗??? 
//  异或为0的表面是两个数相等,而两个数相等的实质是异或为0
// 这就需要我们透过表面看到实质的能力了 ^(* ̄(oo) ̄)^ 
// 所以,nim游戏,只要判断一下是不是所有的堆数异或结果为0即可 
#include <bits/stdc++.h>
using namespace std;
bool jc;
int n,m,t,i,j,ans,x;
int main(){
    scanf("%d",&t);
    for (i=1; i<=t; i++)
    {
        scanf("%d",&n);
        for (register int j=1; j<=n; ++j) scanf("%d",&x),ans=ans^x;
        if (ans==0) cout<<"No"<<endl;
        else cout<<"Yes"<<endl;
    }
return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值