博弈题集(1)

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


//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 2000
set<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 60
int 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积]




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值