这种简单的博弈题目大多都是有规律的,只要能分辨出是哪种题型的,然后用板子一套就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;
}