zoukankan      html  css  js  c++  java
  • 公平组合游戏

    ICG满足以下特征:

    (1)有两个玩家,游戏规则对两个玩家公平

    (2)游戏状态有限,能走的步数也是有限的

    (3)轮流走,当一个玩家不能走时游戏结束

    (4)游戏的局势不能区分玩家的身份,例如黑白棋就是不行的

    特征:

    给定初始局势,指定先手玩家,如果双方都采取最优策略,那么获胜者已经确定了,也就是说ICG问题存在必胜策略

    巴什游戏

    有n个石子,甲先取,乙后取,每次可以拿出1~m颗石子,轮流拿下去,拿到最后一颗的人获胜

    如果:n%(m+1)==0 先手败,否则先手胜

    P-position:前一个玩家刚走一步的必胜位置

    N-position:下一个玩家的必胜位置

    尼姆游戏

    从一堆石头扩展到多维石头

    对于任意的a1,a2....an,一个判断胜负的方法:做异或运算  nim-sum运算

    如果a1^a2...^an!=0 先手必胜,否则先手必败

    hdu 1850  

    下面是一个二人小游戏:桌子上有M堆扑克牌;每堆牌的数量分别为Ni(i=1…M);两人轮流进行;每走一步可以任意选择一堆并取走其中的任意张牌;桌子上的扑克全部取光,则游戏结束;最后一次取牌的人为胜者。
    现在我们不想研究到底先手为胜还是为负,我只想问大家:
    ——“先手的人如果想赢,第一步有几种选择呢?”

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1010;
    const int INF=0x3fffffff;
    typedef long long LL;
    typedef unsigned long long ull;
    int sum=0,ans=0;
    int n;
    int a[110];
    int main(){
        while(scanf("%d",&n)!=EOF){
            if(n==0) break;
            for(int i=1;i<=n;i++) scanf("%d",&a[i]);
            sum=0;ans=0;
            for(int i=1;i<=n;i++){
                sum^=a[i];
            }
            if(sum==0) printf("0
    ");
            else{
                for(int i=1;i<=n;i++){
                    if((sum^a[i])<=a[i]) ans++;
                }
                printf("%d
    ",ans);
            }
            
        }
    return 0;
    }
    View Code

    图游戏和Sprague-Grundy函数

    Sprague-Grundy函数是巴什游戏、尼姆游戏的通用方法,用图作为分析工具

    给定一个有向无环图,在一个起点放一个棋子,两个玩家交替将这个棋子沿着有向边移动,不能移动的就输了

    把每个局势看作为一个点,每个局势和它的后继局势之间有一条有向边,抽象为了图游戏

    !找到关键点,先手毕胜点、先手必败点

    Sprague-Grundy函数

    定义:在一个图G(X,F)中,把节点x的Sprague-Grundy函数定义为sg(x),等于没有指定给它任意后继节点的sg值的最小非负整数

    求每个点的sg值:与用dp求p-positive和n-positive差不多,复杂度O(nm)

    sg(x)=0的节点x是必败点,即p-positive点

    例如:求解巴什游戏

    hdu 1864

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1010;
    const int INF=0x3fffffff;
    typedef long long LL;
    typedef unsigned long long ull;
    //用sg函数求解
    int c,n,m; 
    int a[maxn];
    int sg[maxn];
    void getsg(){
        memset(sg,0,sizeof(sg));
        for(int i=1;i<=n;i++){
            memset(a,0,sizeof(a));
            for(int j=1;j<=m&&i-j>=0;j++) a[sg[i-j]]=1;  //把i的后继节点放到集合a中 
            for(int j=0;j<=n;j++)
            if(!a[j]){//计算sg函数 
                sg[i]=j;break;
            }
        }
    }
    int main(){
        cin>>c;
        while(c--){
            cin>>n>>m;
        //    if(n%(m+1)==0) printf("second
    ");
        //    else printf("first
    ");
        getsg();
        if(sg[n]) printf("first
    ");
        else printf("second
    "); 
        }
    return 0;
    }
    View Code

    求解尼姆游戏

    hdu 1848

    任何一个大学生对菲波那契数列(Fibonacci numbers)应该都不会陌生,它是这样定义的:
    F(1)=1;
    F(2)=2;
    F(n)=F(n-1)+F(n-2)(n>=3);
    所以,1,2,3,5,8,13……就是菲波那契数列。
    在HDOJ上有不少相关的题目,比如1005 Fibonacci again就是曾经的浙江省赛题。
    今天,又一个关于Fibonacci的题目出现了,它是一个小游戏,定义如下:
    1、  这是一个二人游戏;
    2、  一共有3堆石子,数量分别是m, n, p个;
    3、  两人轮流走;
    4、  每走一步可以选择任意一堆石子,然后取走f个;
    5、  f只能是菲波那契数列中的元素(即每次只能取1,2,3,5,8…等数量);
    6、  最先取光所有石子的人为胜者;
    假设双方都使用最优策略,请判断先手的人会赢还是后手的人会赢。

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1010;
    const int INF=0x3fffffff;
    typedef long long LL;
    typedef unsigned long long ull;
    int sg[maxn],s[maxn];
    int fibo[]={1,2,3,5,8,13,21,34,55,89,144,233,377,610,987};
    void getsg(){
        for(int i=0;i<maxn;i++){
            sg[i]=i;
            memset(s,0,sizeof(s));
            for(int j=0;j<15&&i>=fibo[j];j++) s[sg[i-fibo[j]]]=1;
            for(int j=0;j<=i;j++){
                if(!s[j]) {
                    sg[i]=j;break;
                }
            }
        }
        
    }
    
    
    int main(){
        getsg();
        int n,m,p;
        while(cin>>n>>m>>p&&n+m+p){
            if(sg[n]^sg[m]^sg[p]) cout<<"Fibo"<<endl;  //先手赢 
            else cout<<"Nacci"<<endl;
        }
    return 0;
    }
    View Code

    威佐夫游戏

    有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。

    设两堆石子的数量为(a,b),先手必输的局势有(0,0),(1,2),(3,5),(4,7),(6,10),(8,13),(9,15)这些局势称为奇异局势

    奇异局势特征:

    (1)差值递增:1,2,3,4...

    (2)每个局势的第一个数是前面没有出现过的最小的自然数

    每个奇异局势的第一个数是这个局势的差值*黄金分割比例1.618然后取整

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1010;
    const int INF=0x3fffffff;
    typedef long long LL;
    typedef unsigned long long ull;
    
    int main(){
        int n,m;
        double gold=(1+sqrt(5.0))/2;
        while(cin>>n>>m){
            int a=min(n,m);
            int b=max(n,m);
            double k=(double)(b-a);
            int test=(int)(k*gold);   //差值*黄金分割数==?==第一个数 奇异局势是先手必输的局势 
            if(test==a) cout<<"0"<<endl;
            else cout<<"1"<<endl;
        }
    return 0;
    }
    View Code
  • 相关阅读:
    C# WPF 窗体传递消息
    WPF ProgressBar 样式
    WPF的TextBox以及PasswordBox显示水印文字
    Win8.1 Hyper-V 共享本机IP上网
    ASP.NET MVC入门-Startup 类:注册服务和使用中间件
    ASP.NET MVC入门-Program类:程序的入口
    Unity3d AssetBundle 资源加载与管理
    C#考核知识点总结
    数据结构与算法之美-字符串匹配(上)
    LeetCode-探索链表-综合问题
  • 原文地址:https://www.cnblogs.com/shirlybaby/p/13038384.html
Copyright © 2011-2022 走看看