博弈问题之其它博弈合集

找规律博弈:

这类题目出得也很多,而且出得很巧妙,相比固定模式的博弈解决方法,这类题目更加需要开创性思维和强大的逻辑能力。虽然最后可能代码很简单,但其中的思考过程却十分精彩。而且人都有一种在未知情况下的本能就是找出事物的规律,所以这也是人本能的一种体现~

例题:

POJ1740 A New Stone Game

这是楼教主的男人八题之一,非常好的找规律博弈,既不是很简单的一眼题,想法也很巧妙,我的男人八题专题系列有写到这个题目:传送阵


动态博弈:

动态博弈(dynamic game)是指参与人的行动有先后顺序,而且行动在后者可以观察到行动在先者的选择,并据此作出相应的选择。动态博弈的困难在于,在前一刻最优的决策在下一刻可能不再为最优,因此在求解上发生很大的困难。动态博弈行动有先后顺序,不同的参与人在不同时点行动,先行动者的选择影响后行动者的选择空间,后行动者可以观察到先行动者做了什么选择,因此,为了做最优的行动选择,每个参与人都必须这样思考问题:如果我如此选择,对方将如何应对?如果我是他,我将会如何行动?给定他的应对,什么是我的最优选择?如下棋。

其实就是DP+博弈,这类题目将博弈问题的性质糅合在了DP中,其实主要还是DP,状态转移方程是关键。

例题:

POJ1678 I Love this Game!

题意是给你n个数字和a,b(a<b),第一个人拿[a,b]中的数字,然后第二个人拿比这个数字大且相差在[a,b]之间的数字,直到那不了,问最优情况下第一个人拿的点数之和与第二个人的差。这就是一个DP过程,只要后面的人每次都取最大数即可。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<set>
#include<vector>
#include<stack>
#define mem(a,b) memset(a,b,sizeof(a))
#define FOR(a,b,i) for(i=a;i<=b;++i)
#define For(a,b,i) for(i=a;i<b;++i)
#define N 1000000007
#define INF 100000000
using namespace std;
inline void RD(int &ret)
{
    char c;
    do
    {
        c=getchar();
    }
    while(c<'0'||c>'9');
    ret=c-'0';
    while((c=getchar())>='0'&&c<='9')
    {
        ret=ret*10+(c-'0');
    }
}
inline void OT(int a)
{
    if(a>=10)
    {
        OT(a/10);
    }
    putchar(a%10+'0');
}
int num[10001],dp[10001];
int main()
{
    int t,n,i,j,a,b,s;
    RD(t);
    while(t--)
    {
        RD(n);
        RD(a);
        RD(b);
        FOR(1,n,i)
        {
            scanf("%d",&num[i]);
        }
        sort(num+1,num+n+1);
        for(i=n;i>=1&&num[i]>=a;--i)
        {
            s=-INF;
            FOR(i,n,j)
            {
                if(num[i]+b<num[j])
                {
                    break;
                }
                if(num[i]+a<=num[j]&&dp[j]>s)
                {
                    s=dp[j];
                }
            }
            s=s>-INF?s:0;
            dp[i]=num[i]-s;
        }
        s=-INF;
        for(i=1;i<=n&&num[i]<=b;++i)
        {
            if(num[i]>=a&&dp[i]>s)
            {
                s=dp[i];
            }
        }
        s=s>-INF?s:0;
        printf("%d\n",s);
    }
    return 0;
}

图上博弈:

一般是无向图的删边游戏,游戏规则:在一个的无向图内,有一点作为根,两人轮流删边,脱离根的部分不用管,没有边可以删的人失败。团的性质是,删去其中任意一条边,点并不会闪架,它一定还与原图其他边相连。这样,对于这样的团,两人轮流删,一次删一条边,那关键就在于这样的团有多少条边可用来被轮流删了。
    我们还是先看下奇偶吧。
    对于一个单独的偶环,一定是先手必败,其sg=0。
    对于一个单独的奇环,一定是先手必胜,其sg=1。
    那么,在一个环团中,若有偶数条边,一定是先手必败。若有奇数条边,一定是先手必胜。
    实现时,我们可以把环团缩成一个点,环团上的每条边都作为一个与这个代表结点直接连接的一个点。这些点都是图里的子结点,sg=0,那么偶数条边,先手必败,代表结点经环上新生成的子节点异或和正好是0。如果是奇数条边,先手必胜,代表结点经环上新生成的子节点异或和正好是1。

例题:

POJ3710 Christmas Game

这题意思是给出了多个无向图进行删边游戏,且存在成环的情况。问你是谁胜利。这题的正解应该是需要用到Tarjan算法进行缩点,我是图论渣不太会,直接用的是dfs记忆化搜索+强大的sg这题和之前的POJ 2599有些类似,不过这题更麻烦而已。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<set>
#include<vector>
#include<stack>
#include <queue>
#include<map>
#define mem(a,b) memset(a,b,sizeof(a))
#define FOR(a,b,i) for(i=a;i<=b;++i)
#define For(a,b,i) for(i=a;i<b;++i)
using namespace std;
inline void RD(int &ret)
{
    char c;
    do
    {
        c=getchar();
    }
    while(c<'0'||c>'9');
    ret=c-'0';
    while((c=getchar())>='0'&&c<='9')
    {
        ret=ret*10+(c-'0');
    }
}
inline void OT(int a)
{
    if(a>=10)
    {
        OT(a/10);
    }
    putchar(a%10+'0');
}
int m;
int v[101][101],vis[101],num[101];
int dfs(int x,int y)
{
    int s=0,i,j;
    vis[x]=1;
    FOR(1,m,i)
    {
        if(i!=x&&i!=y&&v[x][i]==1)
        {
            if(vis[i]==1)
            {
                num[i]++;
                v[x][i]=v[i][x]=0;
                return -2;
            }
            j=dfs(i,x);
            if(j<0)
            {
                if(num[x]==1)
                {
                    if(j%2!=0)
                    {
                        s^=1;
                    }
                    num[x]--;
                }
                else
                {
                    return --j;
                }
            }
            else
            {
                s^=(j+1);
            }
        }
    }
    return s;
}
int main()
{
    int n,k,i,j,a,b,sum;
    while(scanf("%d",&n)!=EOF)
    {
        sum=0;
        while(n--)
        {
            RD(m);
            RD(k);
            mem(v,0);
            mem(vis,0);
            while(k--)
            {
                RD(a);
                RD(b);
                v[a][b]=1;
            }
            FOR(1,m,i)
            {
                FOR(1,m,j)
                {
                    if(v[i][j]==1&&v[j][i]==1)//去重边
                    {
                        v[i][j]=v[j][i]=0;
                    }
                }
            }
            FOR(1,m,i)
            {
                FOR(1,m,j)
                {
                    if(v[i][j]==1)//重新转化为无向图方便操作
                    {
                        v[j][i]=1;
                    }
                }
            }
            sum^=dfs(1,0);
        }
        if(sum==0)
        {
            printf("Harry\n");
        }
        else
        {
            printf("Sally\n");
        }
    }
    return 0;
}

NIM积:

这个概念实在是太神奇了,看了曹钦翔大神的《从“k倍动态减法游戏”出发探究一类组合游戏问题》才对这个略知了皮毛:传送阵

这个说实在是说不清,还是自己看吧。。。

例题:

POJ3533 Light Switching Game

此题是一个很麻烦的博弈题,有1000*1000*1000的立方体,每个点上有灯,每次选择 (x1,y1,z1), (x1,y1,z2), (x1,y2,z1), (x1,y2,z2), (x2,y1,z1), (x2,y1,z2), (x2,y2,z1), (x2,y2,z2)(1 ≤ x1 ≤ x2 ≤ 1000, 1 ≤y1 ≤ y2 ≤ 1000, 1 ≤z1 ≤ z2 ≤ 1000 )点进行等状态的转换,但要求(x2,y2,z2)必须是有开着的状态,实现给你一些亮着灯的坐标,问后手是否能赢。由于是三维的nim积,所以需要用到Tartan定理,反正我具体也不清楚为什么这么求,就是直接nim_power(x,nimpower(y,z))就行了。套用nim积模板。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<set>
#include<vector>
#include<stack>
#include <queue>
#include<map>
#define mem(a,b) memset(a,b,sizeof(a))
#define FOR(a,b,i) for(i=a;i<=b;++i)
#define For(a,b,i) for(i=a;i<b;++i)
using namespace std;
inline void RD(int &ret)
{
    char c;
    do
    {
        c=getchar();
    }
    while(c<'0'||c>'9');
    ret=c-'0';
    while((c=getchar())>='0'&&c<='9')
    {
        ret=ret*10+(c-'0');
    }
}
inline void OT(int a)
{
    if(a>=10)
    {
        OT(a/10);
    }
    putchar(a%10+'0');
}
int d[1001][1001];
int nim_mul(int x,int y);//两个函数互相调用(nim积模板)
int nim_pow(int x,int y)
{
    if(d[x][y]!=-1)
    {
        return d[x][y];
    }
    if(x==0)
    {
        return d[x][y]=1<<y;
    }
    if(y==0)
    {
        return d[x][y]=1<<x;
    }
    int x1=x,y1=y,s=1,w=1,z;
    while(x1||y1)
    {
        z=1<<w;
        if((x1%2==1||y1%2==1)&&!(x1%2==1&&y1%2==1))
        {
            s*=z;
        }
        w*=2;
        x1/=2;
        y1/=2;
    }
    w=1;
    x1=x;
    y1=y;
    while(x1||y1)
    {
        z=1<<w;
        if(x1%2==1&&y1%2==1)
        {
            s=nim_mul(s,z*3/2);
        }
        w*=2;
        x1/=2;
        y1/=2;
    }
    return d[x][y]=s;
}
int nim_mul(int x,int y)
{
    int i,j,s=0,p=0,q;
    for(p=0,i=x;i;i/=2,p++)
    {
        if(i%2==1)
        {
            for(q=0,j=y;j;j/=2,q++)
            {
                if(j%2==1)
                {
                    s^=nim_pow(p,q);
                }
            }
        }
    }
    return s;
}
int main()
{
    int n,a,b,c,s;
    mem(d,-1);
    while(scanf("%d",&n)!=EOF)
    {
        s=0;
        while(n--)
        {
            scanf("%d%d%d",&a,&b,&c);
            s^=nim_mul(nim_mul(a,b),c);
        }
        if(s==0)
        {
            printf("Yes\n");
        }
        else
        {
            printf("No\n");
        }
    }
    return 0;
}

还有什么不平等博弈,极大极小搜索博弈表示还没搞明白,甚至还不懂,所以以后会了再发吧~

这里给出cxlove大神的博弈总结:无线膜拜~ORZ


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值