取石子问题超全总结

取石子总结

取石子1–巴什博弈

题目:一堆石子,一共共有n个,A和B轮流取,A先手,每次最少取1个,最多取m个,先取完者胜。题目保证两人每一步都是最优策略,问谁先赢?

解释:因为每个人最多能拿m个,而如果剩下m+1个的话无论先取多少个,后面的人一定能都取完,所以每一步都会给对手留下(m+1)的倍数,就能最后取胜。因此只要判断n%(m+1)==0,如果是则B胜,否则A胜。

int n,m;
while(cin>>n>>m)
    if(n%(m+1)==0)
        cout<<"B";
	else
        cout<<"A";
取石子2–尼姆博奕

题目:有n堆石子,每堆石子有任意个,A和B轮流从任意堆里取一定的石子,每次只能选择一个堆且至少取一个,A先取,每一步都是最优策略,先取完者胜,问谁胜?

解释:把每堆物品数全部异或起来,如果得到的值为0,那么后手赢,否则先手赢。

int n;
cin>>n;
int temp=0;
for(int i=0;i<;i++){
	int ans;
	cin>>ans;
	temp^=ans;
}
if(temp==0)
	cout<<"B";
else
	cout<<"A";
取石子3–反尼姆博奕

题目:有n堆石子,每堆石子有任意个,A和B轮流从任意堆里取一定的石子,每次只能选择一个堆且至少取一个,A先取,每一步都是最优策略,先取完者,问谁胜?注意与取石子2的区别。

解释:在反尼姆博奕中判断必胜局面的条件有两点,满足任意一点先手都能取胜,即必胜局面。

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

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

int n;
int ans=0;
int flag=0;
while(n--){
    int m;
    cin>>m;
    ans^=m;
    if(m>1)
        flag=1;
}
if((!ans&&!flag)||(ans&&flag))
    cout<<"A";
else
    cout<<"B";
取石子4–尼姆博弈和巴什博弈的结合

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

解释:有一堆,每堆有n个,每次取1~m个,这是巴什博弈;将一堆扩展到t堆,所以代码如下:

int n,m,t;
int ans=0;
cin>>t;
while(t--){
    cin>>n>>m;
    n=n%(m+1);
    ans^=n;
}
if(ans==0)
    cout<<"B";
else
    cout<<"A";
取石子5–威佐夫博奕

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

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

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<<"B";
else
    cout<<"A";

拓展:

题目与上面相同,但是现在要求前n(n<=10 0000)个必败的状态,比如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的表即可。

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;
cin>>k;
for(i=0;i<=k;i++)//别忘了(0,0)必败
    cout<<a[i][0]<<","<<a[i][1];
  
取石子6–威佐夫博奕

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

解释:首先判断若n==0,则直接输出(0,0);然后 for循环从头到尾把上面Wyothoff Game的第n项公式循环一遍,并分别与n,m比较。

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);

}
取石子7

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

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

#include 
#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<<"B"<<endl;
    else 
        cout<<"A"<<endl;
    return 0;
}
取石子8

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

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

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

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

int main()
{
    int n;
    cin>>n;
    if(n==1 || n==2)cout<<"先手必胜"<<endl;
    else cout<<"先手必败"<<endl;
    return 0;
}
取石子9

题目:有一堆石子,共有n个,A先手,第一轮只能取一个,第二轮之后,对于第m(m>=2)轮,不能一个都不取,且最多只能取m个。当某个玩家取走石子堆中的最后一个石子时,该玩家获得胜利。过程中A、B都使用最优策略,那么谁会胜利?

int n;
cin>>n;
int x=sqrt(n);
if(x<=n&&n<x*(x+1))
    cout<<"A";
else
    cout<<"B";
取石子10

在这里插入图片描述
这题是考察对题目的理解, 如果石头的总数 为奇数那么Bob将无法拿到最后一个,如果是偶数那么Alice将无法拿到最后一个.综上所述 判断 总数 为 奇偶即可.

#include
int main (){
	int n;
	scanf("%d",&n);
	int ans = 0;
	for(int i = 0 ; i < n ; i++){
		int  m;
		scanf("%d",&m);
		ans += m;
	}
	if(ans & 1 == 1) printf("Alice"); //利用位运算判断奇偶
	else printf("Bob");
	return 0;
}

以上是当前整理到的所有取石子问题的解答。参考了多位博主的内容,原文可看下面链接。

原文链接:https://blog.csdn.net/weixin_46075832/article/details/121199703

https://blog.csdn.net/qq_41021816/article/details/84780409

https://blog.csdn.net/qq_63977247/article/details/127603922

你可能感兴趣的:(编程基础,算法,c++,开发语言)