巴什博弈

定义

巴什博弈:一堆物品有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;
}

posted @ 2020-09-23 11:11  hunxuewangzi  阅读(445)  评论(0编辑  收藏  举报