博弈

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\n");
            }
            else{//T2
                printf("Brother\n");
            }
        }
        else{//非奇异
            if(N-ones==0){//S0
                printf("Brother\n");
            }
            else{//S1、S2
                printf("John\n");
            }
        }
    }
    
    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\n");
            }
            else{
                printf("No\n");
            }
        }
        else{
            if(n-ones==0){//S0
                printf("No\n");
            }
            else{
                printf("Yes\n");
            }
        }
    }

    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("\n");
    }
    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\n");
        else
            printf("Fibo\n");
    }
    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\n");
        }
        else{
            printf("second\n");
        }

    }

    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\n");
        }
        else{
            printf("Cici\n");
        }

    }

    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\n");
        }
        else{
            printf("Cici\n");
        }

    }

    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.\n");
        else
            printf("Ollie wins.\n");
    }
    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\n");
       else printf("NO\n");
   }
   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\n");
         else printf("NO\n");
    }
    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!\n");
        else  printf("Rabbit Win!\n");
    }    
    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\n");
            continue;
        }    
        if(m<=n)
        {
            printf("%d",m);
            for(int i=m+1;i<=n;i++)
              printf(" %d",i);
            printf("\n");
            continue;
        }    
        printf("%d\n",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!\n");
        else
            printf("What a pity!\n");
    }
    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\n");
            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\n");
        else  printf("No\n");
    }    
    return 0;
}
View Code

 

posted @ 2016-05-16 21:28  gongpixin  阅读(382)  评论(0编辑  收藏  举报