取石子总结(博弈)

取石子(一)

有一堆石子共有n个,A和B轮流取,A先,每次最少取1个,最多取m个,先取完者胜,A,B足够聪明,问谁先胜?

比较简单的巴什博弈,若n%(m+1)!=0,A胜,否则B胜。

题解:

如果 n=m+1,那么由于一次最多只能取 m 个,所以,无论先取者拿走多少个,后取者都能够一次拿走剩余的物品,后者取胜。只要保持给对手留下(m+1)的倍数,就能最后获胜

#include <bits/stdc++.h>
#define ms(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn = 1e9+5;
const int inf=1<<30;

int main()
{
    int n,m;
    while(cin>>n>>m)
        if(n%(m+1)==0)
            cout<<"后手必胜"<<endl;
        else cout<<"先手必胜"<<endl;
    return 0;
}

取石子(六)

有n堆石子,每堆石子都有任意个,A和B轮流从取任意堆里取一定的石子,每次只能从一堆里至少取一个,A先取,先取完者胜,问谁胜?

题解:

把每堆物品数全部异或起来,如果得到的值为 0,那么先手必败,否则先手必胜

尼姆博弈

#include <bits/stdc++.h>
#define ms(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn = 1e9+5;
const int inf=1<<30;

int main()
{
    int n,m;
    while(cin>>n)
    {
        temp=0;
        for(int i=0; i<n; i++)
        {
            cin>>ans;
            temp^=ans;
        }
        if(temp==0) cout<<"后手必胜"<<endl;
        else cout<<"先手必胜"<<endl;
    }
    return 0;
}

取石子(九)

有n堆石子,每堆石子都有任意个,A和B轮流从任意堆里取一定的石子,每次只能从一堆里至少取一个,A先取,先取完者必败,问谁胜?

题意同取石子(六),不过先取完者败,问谁胜?  注意与Nimm博弈的区别,谁拿完谁输!

题解:

在尼姆博奕中判断必胜局面的条件是所有堆石子数目相异或不等于0 。  而在反尼姆博奕中判断必胜局面的条件有两点,满足任意一点先手都能取胜,即必胜局面。   、

1:各堆石子数目异或结果不等于0(非平衡),且存在有石子数目大于1的石子堆。

 2:各堆石子数目异或结果等于0(平衡),且所有石子堆数目全部为1。

#include <bits/stdc++.h>
#define ms(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn = 1e9+5;
const int inf=1<<30;

int main()
{
    int n,m;
    int ans=0;
    int flag=0;
    cin>>n;
    while(n--)
    {
        cin>>m;
        ans^=m;
        if(m>1)flag=1;
    }
    if((!ans && !flag) || (ans && flag))cout<<"先手必胜"<<endl;
    else cout<<"先手必败"<<endl;
    return 0;
}

取石子(二)

有t堆石子,每堆石子都有n个,A和B轮流从取任意堆里取一定的石子,每次只能从一堆里至少取一个最多取m个,A先取,先取完者胜,问谁胜?(0<=m,n<=2^31))

尼姆博弈和巴什博弈的结合

#include <bits/stdc++.h>
#define ms(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn = 1e9+5;
const int inf=1<<30;

int main()
{
    int n,m;
    int t;
    cin>>t;
    ans=0;
    while(t--)
    {
        cin>>n>>m;
        n=n%(m+1);
        ans^=n;
    }
    if(ans==0) cout<<"后手必胜"<<endl;
    else cout<<"先手必胜"<<endl;

    return 0;
}

取石子(四)

两堆石子分别n,m(n>=m)个,A和B轮流取,有两种取法,一是在任意的一堆中取走任意多的石子,最少为一;二是在两堆中同时取走相同数量的石子。A先取,先取完者胜,问A是否胜?

威佐夫博奕

结论:若(n-m)*(sqrt(5.0)+1.0)/2.0 != m  ,则先手胜,否则后手胜。

#include <bits/stdc++.h>
#define ms(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn = 1e9+5;
const int inf=1<<30;
int main()
{
    int n,m;
    cin>>n>>m;
    if(n<m)n^=m^=n^=m;
    if((n-m)*((sqrt(5.0)+1.0)/2.0)==m)cout<<"后手必胜"<<endl;
    else cout<<"先手必胜"<<endl;
    return 0;
}

拓展一下

Wyothoff Game

 题意同上取石子(四),不过现在要求前n(n<=10W)个必败态,比如A面对(0,0)必败,面对(1,2)(3,5)(4,7)也必败,现在给一个n,求前n个必败态。

他们的差值是递增的,为 0 , 1 , 2, 3, 4 , 5 , 6, 7.....k

差值 * 1.618 == 最小值 的话后手赢,否则先手赢

n表示数量多的堆

m表示数量少的堆

k=(n-m);

k*(sqrt(5.0)+1.0)/2=m

m+k=n 即 k*(sqrt(5.0)+1.0)/2+k=n

第k项(m,n);

并且k的值(差值)是依次递增的

可知其第k项为(k*(sqrt(5.0)+1.0)/2,k*(sqrt(5.0)+1.0)/2+k),由此打一个10W的表即可。

#include <bits/stdc++.h>
#define ms(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn = 1e9+5;
const int inf=1<<30;
int a[100001][2];
int main()
{
    for(int i=1; i<=10000; i++)
        a[i][0]=i*(sqrt(5.0)+1)/2,a[i][1]=a[i][0]+i;
    int k;
    scanf("%d",&k);
    for(i=0; i<=k; i++)
        printf("(%d,%d)",a[i][0],a[i][1]);//注意别忘了(0.0)必败
    printf("\n");

    return 0;
}

取石子(八)

题意取石子(四),依然为威佐夫博弈,但是如果A胜,要你输出你第一次怎么取子,(输出也有若干行,如果最后你是败者,则为0,反之,输出1,并输出使你胜的你第1次取石子后剩下的两堆石子的数量x,y,x<=y。如果在任意的一堆中取走石子能胜同时在两堆中同时取走相同数量的石子也能胜,先输出取走相同数量的石子的情况,假如取一堆的有多种情况,先输出从石子多的一堆中取的情况,且要求输出结果保证第二个值不小于第一个值。)

首先判断若n==0,则直接输出(0,0);

然后 for循环从头到尾把上面Wyothoff Game的第n项公式循环一遍,并分别与n,m比较。

#include <bits/stdc++.h>
#define ms(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn = 1e9+5;
const int inf=1<<30;
int main()
{
    int n,m;
    int temp;
    int temp1;
    cin>>n>>m;
    if(n<m)n^=m^=n^=m;
    int k=n-m;
    int t=k*(sqrt(5.0)+1.0)/2.0;
    if(t==m)
    {
        printf("0\n");
        return 0;
    }
    printf("1\n");
    if(abs(t-m)==abs(t+k-n)&&t<m)//两堆的情况
        printf("%d %d\n",t,t+k);//奇异局势就是剩下来的状态
    if(m==0)
        printf("0 0\n");//0.0也是奇异局势点
    for(int i=0; i<=n; i++) //当在一堆中取得时候
    {
        temp=i*(1+sqrt(5))/2;
        temp1=temp+i;
        
        if(temp==m&&temp1<n)//从b中拿石子,且拿完b比a大 
            printf("%d %d\n",m,temp1);
        else if(temp1==m&&temp<n)//从b中拿石子,但拿完b比a小 
            printf("%d %d\n",temp,m);
        else if(temp1==n&&temp<m)//从a中拿,拿完肯定a还是小于b 
            printf("%d %d\n",temp,n);

    }
        return 0;
    }

 

取石子(五)

有一堆石子,A和B轮流从中取一定的石子,但规定:第一次不能取完,至少一个;从第二次开始,每个人取的石子数至少为1,至多为对手刚取的石子数的两倍。A先取,问A是否会胜?

 结论:若先手对应的石子数目刚好是斐波那契数,则先手必败,否则必胜。

#include <bits/stdc++.h>
#define ms(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn = 1e9+5;
const int inf=1<<30;
ll a[1001]={0,1};
int main()
{
    ll n;
    for(int i=2;i<=100;i++)a[i]=a[i-1]+a[i-2];
    cin>>n;
    int flag=0;
    for(int i=1;i<100;i++)if(a[i]==n)flag=1;
    if(flag)cout<<"先手必败"<<endl;
    else cout<<"先手必胜"<<endl;
    return 0;
}

取石子(七)

 n个石子摆成一圈,A和B轮流取,每次可以从中取一个或相邻两个,先取完者胜,A先取,问谁胜?

 若n==1||n==2 则先手胜,否则后手胜。

假设石子数等于5,如果先者先取一个,那么后者拿走两个,将剩下的两个石子分成两堆,后者赢。如果先者先取二个,那么后者取一个使剩下的两个石子分成两堆,后者赢。

假设石子数等于6,如果先者先取一个,那么后者拿走一个,将剩下的石子分成两段,每段两个,如果先者再拿两个,那么后者赢,如果先者再拿一个,那么后者再取另一堆中的一个,这样剩下的两个石子被分成两堆, 后者赢。         如果先者先取两个,那么后者也取两个使剩下的两个石子分成两堆,后者赢。
所以当先者取走后,后者取走一个或者两个,将剩下的石子分成对称的两段,以此类推,那么如果石子数大于2后者一定赢。

#include <bits/stdc++.h>
#define ms(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn = 1e9+5;
const int inf=1<<30;

int main()
{
    int n;
    cin>>n;

    if(n==1 || n==2)cout<<"先手必胜"<<endl;
    else cout<<"先手必败"<<endl;
    return 0;
}

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值