部分简单博弈总结

这种简单的博弈题目大多都是有规律的,只要能分辨出是哪种题型的,然后用板子一套就OK了。
参考案例:
ZCMUProblem D: 非酋对决
ZCMU1010: Drawing Lines
ZCMU1122: 取石子游戏II
ZCMU1121: 取石子游戏I
ZCMU1461: 天坑的绳子
ZCMU1113: 取石子游戏
洛谷P1247取火柴

一 斐波那契博弈

顾名思义,斐波那契数列的那几项必败。
1.ZCMU1461: 天坑的绳子
天坑有一条长度为n的绳子,天坑想要把这条绳子切成尽可能多的段,并且每段的长度必须为大于等于1的整数而且任意三段绳子都不能组成三角形。请问最多能切几段?
AC代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll f[200];
void fi(int cnt){
    
    
    f[1]=1;f[2]=1;
    for(int i=3;i<=cnt;i++){
    
    
        f[i]=f[i-1]+f[i-2];//数列打表
    }
}
int main() {
    
    
    int T,n;
    fi(150);
    cin>>T;
    for(int i=1;i<=T;i++){
    
    
        cin>>n;
        int k=n;
        int j=1;
        while(n>=f[j]){
    
    //数列打表只是选出哪几段长度可剪的
            n-=f[j];//因此要加个while来逐个减去可减
            j++;
        }
        cout<<j-1<<endl;
    }
    return 0;
}

2.ZCMU1121: 取石子游戏I
一堆石子有n个,两人轮流取.先取者第1次可以取任意多个,但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍。取完者胜.先取者负输出"Second win".先取者胜输出"First win".
AC代码:

#include <bits/stdc++.h>
using namespace std;
int flag=0;
long long f(long long n){
    
    
    long long f=0,f1,f2;
    f1=1;
    f2=1;
    for(int i=2;i<=5000;i++){
    
    
        f=f1+f2;
        f1=f2;
        f2=f;
        if(n==f){
    
    
            flag=1;
            break;
        }
    }
    return flag;
}
int main() {
    
    
    long long n;
    while(cin>>n){
    
    
        flag=0;
        f(n);
        if(flag!=1){
    
    
            cout<<"First win"<<endl;
        }else{
    
    
            cout<<"Second win"<<endl;
        }
    }
    return 0;
}

总结:

斐波那契数列的自定义函数模板:


void figame(int cnt){
    
    
    f[1]=1;f[2]=1;
    for(int i=3;i<=cnt;i++){
    
    
        f[i]=f[i-1]+f[i-2];
    }
}

二 威佐夫博弈

一种关于黄金分割比的博弈
ZCMU1113: 取石子游戏
有两堆石子,数量任意,可以不同。游戏开始由两个人轮流取石子。游戏规定,每次有两种不同的取法,一是可以在任意的一堆中取走任意多的石子;二是可以在两堆中同时取走相同数量的石子。最后把石子全部取完者为胜者。现在给出初始的两堆石子的数目,如果轮到你先取,假设双方都采取最好的策略,问最后你是胜者还是败者。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int WZF(ll a,ll b){
    
    
    double x=(sqrt(5)+1)/2;
    ll k=b-a;
    if(a==(int)(k*x)){
    
    
        return 0;
    }else{
    
    
        return 1;
    }
}
int main(){
    
    
    ll a,b;
    while(cin>>a>>b){
    
    
        if(a>b){
    
    
            swap(a,b);
        }
        cout<<WZF(a,b)<<endl;
    }
    return 0;
}

三 尼姆博弈

一种关于异或和的博弈
1.洛谷P1247取火柴
输入k及k个整数n1​,n2​,…,nk​,表示有k堆火柴棒,第i堆火柴棒的根数为 ni;接着便是你和计算机取火柴棒的对弈游戏。取的规则如下:每次可以从一堆中取走若干根火柴,也可以一堆全部取走,但不允许跨堆取,也不允许不取。
AC代码:

#include <bits/stdc++.h>
using nmaespace std;
int n;
int a[500005];
int main(){
    
    
    scanf("%d",&n);
    int check=0;
    for(int i=1; i<=n; i++){
    
    
        scanf("%d",&a[i]);
        check^=a[i];
    }//读入与累加异或和
    if(check==0){
    
    
        printf("lose\n");
        return 0;
    }//异或和等于0时直接先者输和退出
    for(int i=1; i<=n; i++){
    
    //不然说明异或和等于1时
        if((check^a[i])<a[i]){
    
    
            printf("%d %d\n",a[i]-(check^a[i]),i);//第一次从b中取a个
            for(int j=1;j<=n;j++)//第一次之后的状态
                if(j!=i)
                    printf("%d ",a[j]);
                else    printf("%d ",check^a[i]);
            break;
        }
    }
    return 0;
}

2.洛谷P2197nim游戏
Nim 游戏的规则是这样的:地上有n堆石子(每堆石子数量小于10^4),每人每次可从任意一堆石子里取出任意多枚石子扔掉,可以取完,不能不取。每次只能从一堆里取。最后没石子可取的人就输了。假如甲是先手,且告诉你这n堆石子的数量,他想知道是否存在先手必胜的策略。
输入格式
本题有多组测试数据。
第一行一个整数T(T≤10),表示有 T组数据
接下来每两行是一组数据,第一行一个整数n,表示有n堆石子,n≤10000。第二行有n个数,表示每一堆石子的数量.
输出格式
共T行,如果对于这组数据存在先手必胜策略则输出 Yes,否则输出 No,每个单词一行。
AC代码:

#include<bits/stdc++.h>
using namespace std;
int t,n;
int main(){
    
    
	cin>>t;
	while(t--){
    
    
		cin>>n;
		int ans=0;
		for(int i=1; i<=n; ++i){
    
    
			int shu;
			cin>>shu;
			ans^=shu;//求与覆盖其异或和
		}
		if(ans==0) printf("No\n");//异或和为0时先手必输
		else printf("Yes\n");
	}
}

题目模板:有 n堆数,每堆有m个,每次可以且仅可以取一堆中的若干个数,取先完者为胜,求问先手有没有必胜策略。
结论:当n堆石子的总数量异或和等于0时,先手必败,否则后手必败。

int i;
for(i=2;i<=n;i++){
    
    
a[i]=a[i-1]^a[i];
}
if(a[i-1]==0){
    
    
	cout<<0<<endl;
}else{
    
    
	cout<<1<<endl;
}

猜你喜欢

转载自blog.csdn.net/qq_52480906/article/details/114308178