zoukankan      html  css  js  c++  java
  • 博弈

    hdu 1907 John

    s.anti-nim

    #include<iostream>
    #include<stdio.h>
    using namespace std;
    
    int main(){
    
        int T;
        int N;
        int A;
        int i;
        int sum;
        int ones;//1的个数
    
        scanf("%d",&T);
    
        while(T--){
            scanf("%d",&N);
            sum=0;
            ones=0;
            for(i=0;i<N;++i){
                scanf("%d",&A);
                sum^=A;
                if(A==1){
                    ++ones;
                }
            }
            if(sum==0){//奇异
                if(N-ones==0){//T0
                    printf("John
    ");
                }
                else{//T2
                    printf("Brother
    ");
                }
            }
            else{//非奇异
                if(N-ones==0){//S0
                    printf("Brother
    ");
                }
                else{//S1、S2
                    printf("John
    ");
                }
            }
        }
        
        return 0;
    }
    View Code

    hdu 2509 Be the Winner

    d.我承认题意没读懂。

    s.anti-nim

    #include<iostream>
    #include<stdio.h>
    using namespace std;
    
    int main(){
    
        int n;
        int x;
        int i;
        int sum;
        int ones;
    
        while(~scanf("%d",&n)){
            sum=0;
            ones=0;
            for(i=0;i<n;++i){
                scanf("%d",&x);
                sum^=x;
                if(x==1){
                    ++ones;
                }
            }
            if(sum==0){
                if(n-ones==0){//T0
                    printf("Yes
    ");
                }
                else{
                    printf("No
    ");
                }
            }
            else{
                if(n-ones==0){//S0
                    printf("No
    ");
                }
                else{
                    printf("Yes
    ");
                }
            }
        }
    
        return 0;
    }
    View Code

    hdu 1536 S-Nim

    题意:首先输入K 表示一个集合的大小  之后输入集合 表示对于这对石子只能去这个集合中的元素的个数

    之后输入 一个m 表示接下来对于这个集合要进行m次询问 

    之后m行 每行输入一个n 表示有n个堆  每堆有n1个石子  问这一行所表示的状态是赢还是输 如果赢输入W否则L

    思路:对于n堆石子 可以分成n个游戏 之后把n个游戏合起来就好了
    #include<stdio.h>
    #include<string.h>
    #include<algorithm>
    using namespace std;
    //注意 S数组要按从小到大排序 SG函数要初始化为-1 对于每个集合只需初始化1遍
    //n是集合s的大小 S[i]是定义的特殊取法规则的数组
    int s[110],sg[10010],n;
    int SG_dfs(int x)
    {
        int i;
        if(sg[x]!=-1)
            return sg[x];
        bool vis[110];
        memset(vis,0,sizeof(vis));
        for(i=0;i<n;i++)
        {
            if(x>=s[i])
            {
                SG_dfs(x-s[i]);
                vis[sg[x-s[i]]]=1;
            }
        }
        int e;
        for(i=0;;i++)
            if(!vis[i])
            {
                e=i;
                break;
            }
        return sg[x]=e;
    }
    int main()
    {
        int i,m,t,num;
        while(scanf("%d",&n)&&n)
        {
            for(i=0;i<n;i++)
                scanf("%d",&s[i]);
            memset(sg,-1,sizeof(sg));
            sort(s,s+n);
            scanf("%d",&m);
            while(m--)
            {
                scanf("%d",&t);
                int ans=0;
                while(t--)
                {
                    scanf("%d",&num);
                    ans^=SG_dfs(num);
                }
                if(ans==0)
                    printf("L");
                else
                    printf("W");
            }
            printf("
    ");
        }
        return 0;
    }
    View Code

    hdu 1848 Fibonacci again and again

    题意:取石子问题,一共有3堆石子,每次只能取斐波那契数个石子,先取完石子者胜利,问先手胜还是后手胜

    1. 可选步数为一系列不连续的数,用GetSG(计算) 
    2. 最终结果是所有SG值异或的结果 
    #include<stdio.h>
    #include<string.h>
    #define N 1001
    //f[]:可以取走的石子个数
    //sg[]:0~n的SG函数值
    //hash[]:mex{}
    int f[N],sg[N],hash[N];     
    void getSG(int n)
    {
        int i,j;
        memset(sg,0,sizeof(sg));
        for(i=1;i<=n;i++)
        {
            memset(hash,0,sizeof(hash));
            for(j=1;f[j]<=i;j++)
                hash[sg[i-f[j]]]=1;
            for(j=0;j<=n;j++)    //求mes{}中未出现的最小的非负整数
            {
                if(hash[j]==0)
                {
                    sg[i]=j;
                    break;
                }
            }
        }
    }
    int main()
    {
        int i,m,n,p;
        f[0]=f[1]=1;
        for(i=2;i<=16;i++)
            f[i]=f[i-1]+f[i-2];
        getSG(1000);
        while(scanf("%d%d%d",&m,&n,&p)!=EOF)
        {
            if(m==0&&n==0&&p==0)
                break;
            if((sg[m]^sg[n]^sg[p])==0)
                printf("Nacci
    ");
            else
                printf("Fibo
    ");
        }
        return 0;
    }
    View Code

    hdu 1846 Brave Game

    d.

    1、  本游戏是一个二人游戏;
    2、  有一堆石子一共有n个;
    3、  两人轮流进行;
    4、  每走一步可以取走1…m个石子;
    5、  最先取光石子的一方为胜;

    s.Bash Game

    #include<iostream>
    #include<stdio.h>
    using namespace std;
    
    int main(){
        int C;
        int n,m;
    
        scanf("%d",&C);
    
        while(C--){
            scanf("%d%d",&n,&m);
    
            if(n%(m+1)>0){
                printf("first
    ");
            }
            else{
                printf("second
    ");
            }
    
        }
    
        return 0;
    }
    View Code

    hdu 1847 Good Luck in CET-4 Everybody!

    d.

    1、  总共n张牌;
    2、  双方轮流抓牌;
    3、  每人每次抓牌的个数只能是2的幂次(即:1,2,4,8,16…)
    4、  抓完牌,胜负结果也出来了:最后抓完牌的人为胜者;

    s.

    画出PN图,如下:
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
    N N P N N P N N P N  N   P   N   N  P   N
    上图很容易画出,因为剩余1或2张牌时,后者可以一次取光获胜,当剩余3张牌时,因为后者只能取1张或者2张,所以后者必败,一次类推就可以得出上图的PN图。跟据PN图可以很容易的得出当牌的张数为3的倍数时先手必败,反之先手必胜。

    ps:好神奇,好像转成只能取1和2的Bash Game了。

    #include<iostream>
    #include<stdio.h>
    using namespace std;
    
    int main(){
    
        int n;
    
        while(~scanf("%d",&n)){
    
            if(n%3>0){
                printf("Kiki
    ");
            }
            else{
                printf("Cici
    ");
            }
    
        }
    
        return 0;
    }
    View Code

    c2.没想到的话老实求sg值

    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    using namespace std;
    
    #define N 1024
    
    //f[]:可以取走的石子个数
    //sg[]:0~n的SG函数值
    //_hash[]:mex{}
    int f[N],sg[N],_hash[N];
    void getSG(int n)
    {
        int i,j;
        memset(sg,0,sizeof(sg));
        for(i=1;i<=n;i++)
        {
            memset(_hash,0,sizeof(_hash));
            for(j=1;f[j]<=i;j++)
                _hash[sg[i-f[j]]]=1;
            for(j=0;j<=n;j++)    //求mes{}中未出现的最小的非负整数
            {
                if(_hash[j]==0)
                {
                    sg[i]=j;
                    break;
                }
            }
        }
    }
    
    int main(){
    
        int i;
        f[1]=1;
        for(i=2;i<N;++i){
            f[i]=f[i-1]*2;
        }
        getSG(1005);
    
        int n;
    
        while(~scanf("%d",&n)){
            if(sg[n]>0){
                printf("Kiki
    ");
            }
            else{
                printf("Cici
    ");
            }
    
        }
    
        return 0;
    }
    View Code

    hdu 1517 A Multiplication Game

    d.两个人玩游戏,给一个数字n,每次操作可以从2~9中任选一个数字,并把它与p相乘,(游戏开始时p=1)

    两人轮流操作,当一个人操作完后p>=n,这个人就是胜者。

    s.

    ①、如果输入是29,因为Stan是先手,所以Stan必胜。

    ②、如果输入是1018(9*2),因为Ollie是后手,不管第一次Stan乘的是多少,Stan肯定在29之间,如果Stan乘以2,那么Ollie就乘以9,那么Ollie乘以大于1的数都能超过1018中的任何一个数,Ollie必胜。

    ③、如果输入的是19162(9*2*9),那么这个范围Stan必胜。

    ④、如果输入是163324(9*2*9*2),这个是Ollie的必胜范围。

    …………

    可以发现必胜态是对称的。

    如果“我方”首先给出了一个在N不断除18后的得到不足18的数M,“我方”就可以胜利,然而双方都很聪明,所以这样胜负就决定与N了,如果N不断除18后的得到不足18的数M,如果1<M<=9则先手胜利,即Stan wins.如果9<M<=18则后手胜利。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    
    using namespace std;
    
    int main(){
    
        //freopen("input.txt","r",stdin);
    
        double n;       //用long long 就不能AC了,求解。。。。。。。。
        while(cin>>n){
            while(n>18)
                n/=18;
            if(n<=9)
                printf("Stan wins.
    ");
            else
                printf("Ollie wins.
    ");
        }
        return 0;
    }
    View Code

    hdu 1079 Calendar Game

    ps:还不懂

    d.从当前日期,在他/她转的玩家可以移动到下一个历日或下月的同一天。当在之后的一个月中没有在同一天,播放器只能移动到下一个的日历日期。例如,从1924年12月19日,你可以移动到1924年12月20日,下一个日期,或一月19日,1925年,在同一天在下个月。然而,2001年1月31日,你可以只移动2001年2月1日,因为2001年2月31日是无效的。一个球员赢得比赛时,他/她到底到达的日期2001年11月4日。如果一个玩家移动到日期2001年11月4号之后,他/她输了比赛。

    s.

    (2001 , 11 , 4)是个必败点,能到(2001, 11 , 4)的是必胜点,由时间从后向前推。

    最后若输入的sg[] = 0即为必败点,输出 NO

    #include<iostream>
    #include<string.h>
    using namespace std;
    int sg[500][20][40];
    int day[400];
    int mm[15]={0,31,28,31,30,31,30,31,31,30,31,30,31};
    
    int g(int y , int m , int d)
    {
        if(sg[y][m][d]!=-1) return sg[y][m][d];
        int flag=0;
        if(y>101) return sg[y][m][d]=0;
        if(m==2 && d==day[y] || m<12 && mm[m]==d && mm[m+1]>=d)
        {
            int a=g(y,m+1,d) , b=g(y,m+1,1);
            if(a==0 || b==0) return sg[y][m][d]=1;
            else if(a && b)  return sg[y][m][d]=0;
        }
        else if(m<12 && mm[m]==d && mm[m+1]<d)
        { 
            int a=g(y,m+1,1); 
            if(a==0)  return sg[y][m][d]=1;
            else return sg[y][m][d]=0;
        }
        else if(m==2 && d<day[y] || m<12 && d<mm[m] || m==12 && d<mm[12])
        { //cout<<"**************"<<endl; 
            int a=g(y,m,d+1) , b=g(y , m+1 , d);// cout<<"a= "<<a<<"  b= "<<b<<endl;
            if(a==0 || b==0 ) return sg[y][m][d]=1;
            else if(a && b) return sg[y][m][d]=0;
        }
        else if(m==12 && d==mm[12])
        {
            int a=g(y+1 ,1 ,1) , b=g(y+1 ,1, d);
            if(a==0 || b==0) return sg[y][m][d]=1;
            else if(a && b) return sg[y][m][d]=0;
        }
    }
    
    int main()
    {
       int y,m,d;
       int t;
       memset(sg,-1,sizeof(sg));
       for(int i=1900;i<=2001;i++)
       if(i%4==0&&i%100!=0 || i%400==0) day[i-1900+1]=29; 
       else day[i-1900+1]=28;  
       sg[101][11][4]=0;
       for(int i=5;i<=mm[11];i++) sg[101][11][i]=1;
       for(int i=1;i<=mm[12];i++) sg[101][12][i]=1;
       scanf("%d",&t);
       while(t--)
       {
           scanf("%d%d%d",&y,&m,&d);
           y-=1900; 
           g(y,m,d);// cout<<"**= "<<sg[101][11][4]<<"   "<<sg[101][11][3]<<endl;
           if(sg[y][m][d]==1) printf("YES
    ");
           else printf("NO
    ");
       }
       return 0;
    }
    View Code

    s2.

    找规律,不管是月份加一,还是日期加一,都改变了奇偶性,只有两个特殊日期9月30日,和11月30日例外(不管该年是否为润年,2月28 29同样一步都会发生正常奇偶变化)。

    那么目标日期是11月4日,为奇数。初始日期如果为偶数的话,先者必胜。

    考虑特殊是日期,两个特殊日期本来为奇数,可以做到移动一步还是奇数,那么必胜点与必败点发生变化。

    那么会不会在中途经过这两个日期呢。

    如果本来为偶数,如果经过特殊日期就会改变奇偶,从必胜到必败。作为先手,不会主动进入特殊日期,而后者不可能从奇数依旧到达特殊日期的奇数。

    如果本来为奇数,同样先手想赢,但是不可能进入特殊日期。保持奇偶性交替变化。

    这样一来只可能是初始为特殊日期,否则中途不可能出现特殊日期

    #include<iostream>
    #include<string.h>
    using namespace std;
    int main()
    {
        int y,m,d , t;
        scanf("%d",&t);
        while(t--)
        {
             scanf("%d%d%d",&y,&m,&d);
             if((m+d)%2==0 || m==9 && d==30 || m==11 && d==30)  printf("YES
    ");
             else printf("NO
    ");
        }
        return 0;
    }
    View Code

    hdu 1849 Rabbit and Grass

    s.nim

    #include<stdio.h>
    int main()
    {
        int ans,n,a;
        while(scanf("%d",&n),n)
        {
            ans=0;//因为一个数和0的异或等于本身,所以可以赋初值为0 
            while(n--)
            {
                scanf("%d",&a);
                ans=ans^a;
            }    
            if(ans==0)  printf("Grass Win!
    ");
            else  printf("Rabbit Win!
    ");
        }    
        return 0;
    }
    View Code

    hdu 2149 Public Sale

    s.Bash Game

    #include<stdio.h>
    int main()
    {
        int n,m;
        while(scanf("%d%d",&m,&n)!=EOF)
        {
            if(m%(n+1)==0)
            {
                printf("none
    ");
                continue;
            }    
            if(m<=n)
            {
                printf("%d",m);
                for(int i=m+1;i<=n;i++)
                  printf(" %d",i);
                printf("
    ");
                continue;
            }    
            printf("%d
    ",m%(n+1));
        }    
        return 0;
    }
    View Code

    hdu 2147 kiki's game

    在一个n*m的棋盘内,从(1,m)点出发,每次可以进行的移动是:左移一,下移一,左下移一。然后kiki每次先走,判断kiki时候会赢(对方无路可走的时候)。

    我们可以把PN状态的点描绘出来::

    #include"stdio.h"
    
    int main( )
    {
        int n,m;
        while(scanf("%d%d",&n,&m)&&(n!=0||m!=0))
        {
            if(n%2==0||m%2==0)
                printf("Wonderful!
    ");
            else
                printf("What a pity!
    ");
        }
        return 0;
    }
    View Code

    hdu 1404 Digital Deletions

    一串由0~9组成的数字,可以进行两个操作:1、把其中一个数变为比它小的数;2、把其中一个数字0及其右边的所有数字删除。
    两人轮流进行操作,最后把所以数字删除的人获胜,问前者胜还是后者胜。
    字符串长度为1-6,前者胜输出Yes,否则输出No.

    s.

    从小到大记录,将一步能到达必败点P点的都记为必胜点N点(sg为1 的 点),这样每次遇到的第一个sg为0的点就是必败点。

    求出所有的sg值即可。

    /*
    HDU 1404
    */
    #include<stdio.h>
    #include<math.h>
    #include<iostream>
    #include<string.h>
    using namespace std;
    
    const int MAXN=1000000;
    int sg[MAXN];
    int get_length(int n)//得到整数n的位数 
    {
        if(n/100000) return 6;
        if(n/10000) return 5;
        if(n/1000)  return 4;
        if(n/100)   return 3;
        if(n/10)  return 2;
        return 1;
    }    
    
    void _extend(int n)//sg[n]=0;表示n为后者必胜
                       //那么所以可以一步变成n的都是前者必胜 
    {
        int len=get_length(n);
        int i;
        for(i=len;i>=1;i--)//每一个位上加上一个数 
        {
            int m=n;
            int base=1;
            for(int j=1;j<i;j++)  base*=10;
            int tmp=(m%(base*10))/base;
            for(int j=tmp;j<9;j++)
            {
                m+=base;
                sg[m]=1;//m为前者必胜点 
            }    
        }  
        if(len!=6)//长度小于6,则可以在后面加0开头的
        {
            int m=n;
            int base=1;
            for(int i=len;i<6;i++)
            {
                m*=10;
                for(int b=0;b<base;b++)
                    sg[m+b]=1;
                base*=10;
            }    
        }      
    }   
    void fun()
    {
        memset(sg,0,sizeof(sg));
        sg[0]=1;
        for(int i=1;i<MAXN;i++)
        {
            if(!sg[i])  _extend(i);
        }    
    }  
    int main()
    {
        char str[8];
        int n;
        fun();
        while(scanf("%s",&str)!=EOF)
        {
            if(str[0]=='0')  //第一个数字是0,则前者必胜 
            {
                printf("Yes
    ");
                continue;
            }    
            int len=strlen(str);//第一个数字非0,再转化成整型数 
            n=0;
            for(int i=0;i<len;i++)
            {
                n*=10;
                n+=str[i]-'0';
            }        
            if(sg[n]) printf("Yes
    ");
            else  printf("No
    ");
        }    
        return 0;
    }
    View Code
  • 相关阅读:
    IIS无法显示 XML 页
    asp.net实现sql存取图片
    微软的面试题
    IIS配置.Net问题大全
    Asp.Net调用WebService
    生活本无常,前路更精彩
    【转载】碰到讨厌的老板怎么办
    xxxx不必xx尽,留些xxxx
    【BLOG】Mr梵谷
    机会,从来都是留给有准备的你
  • 原文地址:https://www.cnblogs.com/gongpixin/p/5499558.html
Copyright © 2011-2022 走看看