博弈题集(1)

来源:互联网 发布:nfc手机读写软件 编辑:程序博客网 时间:2024/05/01 18:42

题目:看到 这里 的一个分类,打算分四次把它搞定吧,现在看第一部分的题:


//HDOJ1079 Calendar Game

具体情况具体分析就好,这题数据蛮弱,之前写错了也过了。

#include <iostream>  #include <cstring>  #include <cmath>  #include <cstdio>  #include <algorithm>  using namespace std;  int main()  {      int t,y,m,d,tmp;      bool tag;      scanf("%d",&t);      while(t--)      {          tag=false;;          scanf("%d%d%d",&y,&m,&d);          tmp=m+d;          if(y==2001 && m==11 && d==4)              tag=false;          else if((m==1||m==3||m==5||m==7)&&d==31) // 8.31 10.31 和 12.31 果断抛弃              tag=true;          else if((m==4||m==6||m==9||m==11)&&d==30) //后续可选择为奇态              tag=true;          // 对于2.28的不管是不是闰年,后续可以是3.28为奇态          else if(tmp%2==0)              tag=true;          if(tag)              printf("YES\n");          else              printf("NO\n");      }      return 0;  }  


//HDOJ1525&POJ2348 Euclid's Game

watashi翻译的那本书上有讲过,考虑一下没有选择的时候就快接近答案了。

#include <cstring>#include <algorithm>#include <cmath>#include <iostream>#include <cstdio>#include <vector>using namespace std;bool win;void solve(int a,int b){    if(a>b)        swap(a,b);    if(b%a==0 || b/a>1)        return ;    win=!win;    b=b%a;    solve(a,b);}int main(){    int a,b;    while(scanf("%d%d",&a,&b),a+b)    {        win=1;        solve(a,b);        if(win)            printf("Stan wins\n");        else            printf("Ollie wins\n");    }    return 0;}


//HDOJ1564 Play a game

水题

#include <cstring>#include <algorithm>#include <cmath>#include <iostream>#include <cstdio>#include <vector>using namespace std;int main(){    int n;    while(scanf("%d",&n)!=EOF,n)    {        if(n&1)            printf("ailyanlu\n");        else            printf("8600\n");    }    return 0;}


//HDOJ1846 Brave Game

简单巴什博弈

#include <cstring>#include <algorithm>#include <cmath>#include <iostream>#include <cstdio>#include <vector>using namespace std;int main(){    int t;    scanf("%d",&t);    int n,m;    while(t--)    {        scanf("%d%d",&n,&m);        if(n%(m+1))            printf("first\n");        else            printf("second\n");    }    return 0;}


//HDOJ1847 Good Luck in CET-4 Everybody!

枚举一下必败态和必胜态,或者用sg函数也可以解释

#include <cstring>#include <algorithm>#include <cmath>#include <iostream>#include <cstdio>#include <vector>#include <set>using namespace std;set<int>s;int main(){    s.clear();    int two=1;    for(;two<=1000;two*=2)        s.insert(two);    for(int i=1;i<=1000;i++)    {        if(s.count(i))            continue;        for(set<int>::iterator it=s.begin();it!=s.end();it++)        {            if(*it+i<=1000)                s.insert(*it+i);        }    }    int n;    while(scanf("%d",&n)!=EOF)    {        if(s.count(n))            printf("Kiki\n");        else            printf("Cici\n");    }    return 0;}


//HDOJ2147 kiki's game

跟圆桌那个差不多,取中心,然后跟着对手取对称的。

#include <cstring>#include <algorithm>#include <cmath>#include <iostream>#include <cstdio>#include <vector>#include <set>using namespace std;int main(){    int n,m;    while(scanf("%d%d",&n,&m)!=EOF,m+n)    {        if((n&1)&&(m&1))            printf("What a pity!\n");        else            printf("Wonderful!\n");    }    return 0;}


//HDOJ2516

简单sg函数

#include <cstring>#include <algorithm>#include <cmath>#include <iostream>#include <cstdio>#include <vector>#include <set>using namespace std;set<int>s;int main(){    s.clear();    int x=1,y=1,tmp=x+y;    while(tmp>0)    {        s.insert(tmp);        x=y;        y=tmp;        tmp=x+y;    }    int n;    while(scanf("%d",&n),n)    {        if(s.count(n))            printf("Second win\n");        else            printf("First win\n");    }    return 0;}


//HDOJ2897 

sg打表找规律,或者理解成巴什博弈

#include <cstring>#include <cstdio>#include <iostream>#include <algorithm>#include <cmath>#include <set>using namespace std;int main(){    int n,p,q;    while(scanf("%d%d%d",&n,&p,&q)!=EOF)    {        int tmp=n%(p+q);        if(tmp==0 || tmp>p)            printf("WIN\n");        else            printf("LOST\n");    }    return 0;}


//HDOJ3032 Nim or not Nim? 

sg打表

// SG博弈/*#include <cstring>#include <iostream>#include <cmath>#include <algorithm>#include <cstdio>#include <set>using namespace std;#define maxn 2000set<int>s;int sg[maxn];int solve(){    int i=0;    while(s.count(i))        i++;    return i;}int main(){    int n;    scanf("%d",&n);    sg[0]=0;    for(int i=0;i<=n;i++)    {        s.clear();        for(int j=0;j<i;j++)        {            s.insert(sg[j]);            s.insert(sg[j]^sg[i-j]);        }        sg[i]=solve();    }    for(int i=1;i<=n;i++)        cout<<i<<":"<<sg[i]<<endl;}*/#include <cstring>#include <cmath>#include <cstdio>#include <algorithm>#include <iostream>using namespace std;int sg(int n){    if(n%4==0)        return n-1;    if(n%4==3)        return n+1;    return n;}int main(){    int t;    scanf("%d",&t);    while(t--)    {        int n;        scanf("%d",&n);        int ans=0,x;        for(int i=0;i<n;i++)        {            scanf("%d",&x);            ans^=sg(x);        }        if(ans)            printf("Alice\n");        else            printf("Bob\n");    }    return 0;}


//HDOJ3389 Game

1,3,4是不能够移动的终点。

// 打表找出最终不可移动的点// 找出移动到不可移动点的编号的规律,转化成nim/*#include <cstring>#include <iostream>#include <cmath>#include <algorithm>#include <cstdio>#include <set>using namespace std;#define maxn 60int st[maxn];int main(){    memset(st,0,sizeof(st));    for(int i=1;i<maxn;i++)        for(int j=i+1;j<maxn;j++)            if((i+j)%3==0 && ((i+j)/3)&1 )                st[j]=1;    printf("输出不能移动的位置:\n");    for(int i=1;i<maxn;i++)        if(st[i]==0) // 输出不能移动的位置            cout<<i<<" ";    puts("");    printf("可以移动的:\n");    for(int i=1;i<maxn;i++)        for(int j=i+1;j<maxn;j++)            if((i+j)%3==0 && ((i+j)/3)&1 )                cout<<i<<" <- "<<j<<endl;    return 0;}*/// 发现对于能一次到达1,3,4状态的编号都是mod6的数#include <cstring>#include <cmath>#include <cstdio>#include <algorithm>#include <iostream>#include <vector>using namespace std;int main(){    int t;    scanf("%d",&t);    for(int cas=1;cas<=t;cas++)    {        int n,x;        scanf("%d",&n);        int ans=0;        for(int i=1;i<=n;i++)        {            scanf("%d",&x);            if(i%6==2 || i%6==0 || i%6==5)                ans^=x;        }        printf("Case %d: ",cas);        if(ans)            printf("Alice\n");        else            printf("Bob\n");    }    return 0;}


//HDOJ3537 Daizhenyang's Coin

MT博弈

// Mock Turtles// http://blog.sina.com.cn/s/blog_8f06da99010125ol.html// http://blog.csdn.net/acm_cxlove/article/details/7854181#include <cstring>#include <cmath>#include <iostream>#include <algorithm>#include <cstdio>#include <set>using namespace std;set<long long>s;int main(){    int n;    while(scanf("%d",&n)!=EOF)    {        s.clear();        long long ans=0;        long long x;        for(int i=0;i<n;i++)        {            scanf("%I64d",&x);            s.insert(x);        }        set<long long>::iterator it=s.begin();        for(;it!=s.end();it++)        {            if(__builtin_popcount(2* *it)&1)                ans^=2* *it;            else                ans^=2* *it+1;        }        if(ans)            printf("No\n");        else            printf("Yes\n");    }}


//HDOJ3544 Alice's Game

策略:每次尽量均分

#include <cstring>#include <cmath>#include <cstdio>#include <algorithm>#include <iostream>#include <vector>#include <map>using namespace std;int main(){    int t;    scanf("%d",&t);    for(int cas=1;cas<=t;cas++)    {        int n;        scanf("%d",&n);        long long ansx=0,ansy=0,x,y;        while(n--)        {            scanf("%I64d%I64d",&x,&y);            while(x>1 && y>1)            {                x/=2;                y/=2;            }            if(x==1)                ansy+=y-1;            if(y==1)                ansx+=x-1;        }        printf("Case %d: ",cas);        if(ansx>ansy)            printf("Alice\n");        else            printf("Bob\n");    }    return 0;}


//HDOJ3863 No Gambling

永远必胜

#include <cstring>#include <cmath>#include <cstdio>#include <algorithm>#include <iostream>#include <vector>#include <map>using namespace std;int main(){    int n;    while(scanf("%d",&n),n!=-1)    {        printf("I bet on Oregon Maple~\n");    }    return 0;}


//HDOJ3951 Coin Game 

一个硬币的时候判奇偶,两个及以上硬币的时候转化成跟watashi翻译那本书上那个博弈一样,第一次不管对手怎么拿,我们都可以转化成相同的两条链,然后跟对手一样对称的拿。

#include <cstring>#include <cmath>#include <cstdio>#include <algorithm>#include <iostream>#include <vector>using namespace std;int a[100];int main(){    int t;    int n,k;    scanf("%d",&t);    for(int cas=1;cas<=t;cas++)    {        scanf("%d%d",&n,&k);        printf("Case %d: ",cas);        if(k==1)        {            if(n&1)                printf("first\n");            else                printf("second\n");            continue;        }        if(n-k<=0)            printf("first\n");        else            printf("second\n");    }    return 0;}


//HDOJ2188 悼念512汶川大地震遇难同胞——选拔志愿者

简单巴什博弈

#include <cstring>#include <cmath>#include <cstdio>#include <algorithm>#include <iostream>#include <vector>using namespace std;int main(){    int t;    scanf("%d",&t);    while(t--)    {        int n,m;        scanf("%d%d",&n,&m);        if(n%(m+1)==0)            printf("Rabbit\n");        else            printf("Grass\n");    }    return 0;}


//HDOJ2149 Public Sale

巴什博弈第一步取法

#include <cstring>#include <cmath>#include <cstdio>#include <algorithm>#include <iostream>#include <vector>using namespace std;int main(){    int n,m;    while(scanf("%d%d",&n,&m)!=EOF)    {        if(n%(m+1)==0)            printf("none\n");        else        {            if(n/(m+1)==0)            {                for(int i=n;i<m;i++)                    printf("%d ",i);                printf("%d\n",m);            }            else            {                printf("%d\n",n%(m+1));            }        }    }    return 0;}


//HDOJ1850 Being a Good Boy in Spring Festival

nim博弈取法

#include <cstring>#include <cmath>#include <cstdio>#include <algorithm>#include <iostream>#include <vector>using namespace std;int a[100];int main(){    int n,x;    while(scanf("%d",&n),n)    {        int ans=0,hah=0;        for(int i=0;i<n;i++)        {            scanf("%d",&a[i]);            ans^=a[i];        }        for(int i=0;i<n;i++)        {            if((ans^a[i])<a[i]) // 表明一开始选这个可以将ans二进制中某个1变成0                hah++;        }        printf("%d\n",hah);    }    return 0;}


//HDOJ2176 取(m堆)石子游戏

nim博弈取法

#include <cstring>#include <cmath>#include <cstdio>#include <algorithm>#include <iostream>#include <vector>using namespace std;int a[200001];int main(){    int n;    while(scanf("%d",&n)!=EOF,n)    {        int ans=0;        for(int i=0;i<n;i++)        {            scanf("%d",&a[i]);            ans^=a[i];        }        if(ans==0)        {            printf("No\n");            continue;        }        printf("Yes\n");        for(int i=0;i<n;i++)        {            if((ans^a[i])<a[i])                printf("%d %d\n",a[i],ans^a[i]);        }    }    return 0;}


//HDOJ1527&POJ1067 取石子游戏

简单威佐夫博弈

#include <cstring>#include <cmath>#include <cstdio>#include <algorithm>#include <iostream>#include <vector>using namespace std;double tmp=(sqrt(5.0)+1)/2;int main(){    int n,m;    while(scanf("%d%d",&n,&m)!=EOF)    {        if(n>m)            swap(n,m);        if(int((m-n)*tmp)==n)            printf("0\n");        else            printf("1\n");    }    return 0;}


//HDOJ2177 取(2堆)石子游戏

威佐夫博弈第一步取法

#include <cstring>#include <cmath>#include <cstdio>#include <algorithm>#include <iostream>#include <vector>#include <map>using namespace std;double tmp=(sqrt(5.0)+1)/2;map<int,int>mp;map<int,int>ans;int main(){    mp.clear();    for(int i=0;i<1000010;i++)        mp[(int)i*tmp]=(int)i*tmp+i;    map<int,int>::iterator it=mp.begin();    int n,m;    while(scanf("%d%d",&n,&m)!=EOF,m+n)    {        ans.clear();        if(mp[n]==m)            printf("0\n");        else        {            printf("1\n");            map<int,int>::iterator it=mp.begin(),ii=mp.end(),jj=mp.end();            for(;it!=mp.end() && (it->first<=m);it++)            {                if(it->second==m)                {                    ii=it;                }                else if(it->second==n)                {                    jj=it;                }                if(n-it->first == m-it->second && m-it->second>0)                    ans[it->first]=it->second;            }            for(it=ans.begin();it!=ans.end();it++)            {                printf("%d %d\n",it->first,it->second);            }            if(mp[n]&&mp[n]<m)                printf("%d %d\n",n,mp[n]);            if(ii!=mp.end()&&ii->first<n)                printf("%d %d\n",ii->first,m);            if(jj!=mp.end())                printf("%d %d\n",jj->first,jj->second);        }    }    return 0;}


//HDOJ1517&POJ2505 A Multiplication Game

假设当前状态时必败态,然后一直往前推,能够到达必败态的是必胜态,只能到达必胜态的是必败态,枚举一下区间即可。

#include <cstring>#include <cmath>#include <cstdio>#include <algorithm>#include <iostream>#include <vector>#include <map>using namespace std;int main(){    long long n;    while(scanf("%I64d",&n)!=EOF)    {        bool ans=0;        while(n>1)        {            if(!ans)                n=(n-1)/9+1;            else                n=(n-1)/2+1;            ans^=1;        }        if(ans)            printf("Stan wins.\n");        else            printf("Ollie wins.\n");    }    return 0;}


//HDOJ2486&HDOJ2580&POJ3922 A simple stone game

K倍动态减法博弈,具体证明看论文吧。

#include <cstring>#include <iostream>#include <algorithm>#include <cmath>#include <cstdio>using namespace std;const int maxn = 1000010 ;int a[maxn],b[maxn];int n,k;int solve(int n){    int i=0,j=0;    a[0]=1,b[0]=1;    while(a[i]<n)    { // a[i]表示当前能用来构造的最大项      // b[i]表示由0...i,由a中数列所构成的最大值        i++;        a[i]=b[i-1]+1; // b[i-1]+1不能表示成a中的前i-1中某不连续几项的和,故需要构造        while(a[j+1]*k<a[i])            j++; // 找到最近的恰好与第i项差值在k倍以上的        if(a[j]*k<a[i])            b[i]=b[j]+a[i]; //用到了a中前i-2项,保证和为a中某不连续的话,可以取当前的j        else // 倒数第二项和最后一项差值恰好为k倍时,能构造的最大项不变            b[i]=a[i];    }    if(n==a[i])        return -1;    int ans;   while(n)   {       if(n>=a[i])            n-=a[i],ans=a[i];       i--;   }   return ans;}int main(){    int t;    scanf("%d",&t);    for(int cas=1;cas<=t;cas++)    {        scanf("%d%d",&n,&k);        int ans = solve(n);        printf("Case %d: ",cas);        if(ans==-1)            printf("lose\n");        else            printf("%d\n",ans);    }    return 0;}


//HDOJ4315 Climbing the Hill

转化一下,类似于watashi翻译那本书上的一个nim博弈,好像正规叫法是阶梯博弈,特殊情况就是要讨论一下,k=1和k=2的情况,具体见代码

#include <cstring>#include <cmath>#include <cstdio>#include <algorithm>#include <iostream>#include <vector>#include <map>using namespace std;int a[1010];int main(){    int n,k;    while(scanf("%d%d",&n,&k)!=EOF)    {        int ans=0;        for(int i=1;i<=n;i++)        {            scanf("%d",&a[i]);        }        if(k==1)        {            printf("Alice\n");            continue;        }        if(n&1)        {            ans^=a[1]-(k==2);            for(int i=2;i<n;i+=2)                ans^=(a[i+1]-a[i]-1);        }        else        {            for(int i=1;i<=n;i+=2)                ans^=(a[i+1]-a[i]-1);        }        if(ans)            printf("Alice\n");        else            printf("Bob\n");    }    return 0;}



最后两道没来及学新知识,周末过了稍微闲点的时候再补上。


HDOJ1538 A Puzzle for Pirates [海盗分金问题]
HDOJ3404 Switch lights [Nim积]




原创粉丝点击