巴什博弈
定义
巴什博弈:一堆物品有n个,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个。最后取光者得胜。
题目链接1
题目思路
这是一个裸的巴什博弈,但是数据为1<=n,m<=1000
显然很容易想到一个\(n^2\)的dp转移,如果一个状态只能转移到必胜态,那么这个状态就是必败态,否则就是必胜态
代码
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#define fi first
#define se second
#define debug printf(" I am here\n");
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int maxn=1e3+5,inf=0x3f3f3f3f;
const double eps=1e-10;
int n,m;
bool dp[maxn];
signed main(){
int _;scanf("%d",&_);
while(_--){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
dp[i]=1;
for(int j=1;j<=min(m,i);j++){
dp[i]=dp[i]&dp[i-j];
}
dp[i]=!dp[i];
}
printf(dp[n]?"first\n":"second\n");
}
return 0;
}
然而如果n,m更大那么该怎么办,这个时候就要运用到巴什博弈。显然打表就可以发现规律
引用百度百科的解释:
巴什博弈:只有一堆n个物品,两个人轮流从这堆物品中取物,规定每次至少取一个,最多取m个。最后取光者得胜。
显然,如果n=m+1,那么由于一次最多只能取m个,所以,无论先取者拿走多少个,后取者都能够一次拿走剩余的物品,后者取胜。因此我们发现了如何取胜的法则:如果n=(m+1)r+s,(r为任意自然数,s≤m),那么先取者要拿走s个物品,如果后取者拿走k(≤m)个,那么先取者再拿走m+1-k个,结果剩下(m+1)(r-1)个,以后保持这样的取法,那么先取者肯定获胜。总之,要保持给对手留下(m+1)的倍数,就能最后获胜。
代码
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#define fi first
#define se second
#define debug printf(" I am here\n");
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int maxn=1e3+5,inf=0x3f3f3f3f;
const double eps=1e-10;
int n,m;
bool dp[maxn];
signed main(){
int _;scanf("%d",&_);
while(_--){
scanf("%d%d",&n,&m);
bool flag=n%(m+1)?1:0;
printf(flag?"first\n":"second\n");
}
return 0;
}
题目链接2
题目大意
巴什博弈的变形,不过每次取的数量为2的幂次。
题目思路
显然由于数据小,可以直接dp状态转移,思路同上
代码
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#define fi first
#define se second
#define debug printf(" I am here\n");
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int maxn=1e3+5,inf=0x3f3f3f3f;
const double eps=1e-10;
int n;
bool dp[maxn];
signed main(){
while(scanf("%d",&n)!=-1){
for(int i=1;i<=n;i++){
dp[i]=1;
for(int j=1;(1<<(j-1))<=i;j++){
int x=(1<<(j-1));
dp[i]=dp[i]&dp[i-x];
}
dp[i]=!dp[i];
}
printf(dp[n]?"Kiki\n":"Cici\n");
}
return 0;
}
如果数据大怎么办 打表找规律啊
你会发现n%3==0,则Cici胜,否则Kiki胜
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#define fi first
#define se second
#define debug printf(" I am here\n");
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int maxn=1e3+5,inf=0x3f3f3f3f;
const double eps=1e-10;
int n;
signed main(){
while(scanf("%d",&n)!=-1){
bool flag=n%3?1:0;
printf(flag?"Kiki\n":"Cici\n");
}
return 0;
}
题目链接3
题目大意
这个也是巴什博弈的变形,只不过每次取只能取[L,R]的石子,如果剩下少于l枚就要一次取完
题目思路
想不出,果断打表
打表是可以找出规律的
下面证明
x=n%(L+R)
1:首先如果x==0,那么先手可以先选R个,那么后手任选k,先手再选L+R-k,那么最后后手一定剩余L个,后手必输
2:如果1<=x<=L,先手随便选k个,那么后手就可选L+R-x个,则先手必定最后剩下x个,先手必输
3:如果x>L,先手选k个使1<=x-k<=L个,那么后手就进入了第二个状态,则后手必败
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<vector>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#define fi first
#define se second
#define debug printf(" I am here\n");
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
const ll INF=0x3f3f3f3f3f3f3f3f;
const int maxn=1e3+5,inf=0x3f3f3f3f;
const double eps=1e-10;
int n,l,r;
signed main(){
while(scanf("%d%d%d",&n,&l,&r)!=-1){
int rest=n%(l+r),flag;
if(rest==0||(rest>l)){
flag=1;
}else{
flag=0;
}
printf(flag?"WIN\n":"LOST\n");
}
return 0;
}