洛谷题单 Part 6.4 博弈论

比赛博弈论被打烂了,场场有博弈论,场场找规律找几个小时,这两天系统地学一下博弈论,先拿这个洛谷小题单练练手。

P2197 【模板】nim 游戏

题面
s g sg sg定理的模板题, s g sg sg函数为 s g ( i ) = a i sg(i)=a_i sg(i)=ai,答案取异或和即可。

#include 
using namespace std;
inline void read(int &x){
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+(ch&15);ch=getchar();}
    x=s*w;
}
int t,n,x,ans;
int main(){
    read(t);
    while(t--){
        ans=0;
        read(n);
        for(int i=1;i<=n;i++)read(x),ans^=x;
        if(ans)puts("Yes");
        else puts("No");
    }
}

P1288 取数游戏 II

题面
考虑当前取到 i i i,前面的已经取过的都为 0 0 0,那么很明显只能向后取,并且每次操作必须完所有的数。

那么对于一个链, a 1 , a 2 , a 3 , . . . , a n , 0 a_1,a_2,a_3,...,a_n,0 a1,a2,a3,...,an,0,先手从左到右取,当且仅当 2 ∤ n 2\nmid n 2n时获胜,否则后手获胜,因此我们只需要在起点枚举向前向后取数即可。

#include 
using namespace std;
inline void read(int &x){
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+(ch&15);ch=getchar();}
    x=s*w;
}
int n,a[100];
int main(){
    read(n);
    for(int i=1;i<=n;i++)read(a[i]);
    for(int i=1;i<=n;i++){
        if(!a[i]){
            if(i%2==0){
                puts("YES");
                return 0;
            }
            break;
        }
    }
    for(int i=n;i;i--){
        if(!a[i]){
            if((n-i+1)%2==0){
                puts("YES");
                return 0;
            }
            break;
        }
    }
    puts("NO");
}

P1290 欧几里德的游戏

题面
对于当前数对 ( n , m ) (n,m) (n,m),不妨设 m < n mm<n

如果 ⌊ n m ⌋ > 1 \lfloor\frac{n}{m}\rfloor>1 mn>1,即可以对 ( n , m ) (n,m) (n,m)操作两次及以上,那么很显然,如果 ( m , n    m o d    m ) (m,n\,\,mod\,\,m) (m,nmodm)为必胜态,我们只需操作到 ( n , m ) (n,m) (n,m)只剩一次,那么就为对手必败态。
反之如果 ( m , n    m o d    m ) (m,n\,\,mod\,\,m) (m,nmodm)为必败态,那么只需操作到 ( m , n    m o d    m ) (m,n\,\,mod\,\,m) (m,nmodm),此时对手必败。
因此当 ⌊ n m ⌋ > 1 \lfloor\frac{n}{m}\rfloor>1 mn>1时先手必胜。

对于 ⌊ n m ⌋ = 1 \lfloor\frac{n}{m}\rfloor=1 mn=1的情况,只能取 ( m , n    m o d    m ) (m,n\,\,mod\,\,m) (m,nmodm),因此先手必胜/必败与 m , n    m o d    m ) m,n\,\,mod\,\,m) m,nmodm)呈相反关系。

#include 
using namespace std;
inline void read(int &x){
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+(ch&15);ch=getchar();}
    x=s*w;
}
int n,m,t;
bool gcd(int a, int b){
    if(!b)return 0;
    if(a/b==1)return !gcd(b,a%b);
    else return 1;
}
int main(){
    read(t);
    while(t--){
        read(n),read(m);
        if(m>n)swap(n,m);
        if(gcd(n,m))puts("Stan wins");
        else puts("Ollie wins");
    }
}

P1247 取火柴游戏

题面
这题比较有意思,就是 N i m Nim Nim游戏,但是要求输出步数。
考虑到必胜态为 a i a_i ai的异或和不为零,因此当异或和为 0 0 0时即为必败态,直接输出 l o s e lose lose
对于必胜态,设总异或和为 s u m sum sum,则对于 a i a_i ai,除 a i a_i ai的异或和为 s u m ⊕ a i sum\oplus a_i sumai,我们想让下一步为必败态,即序列区间异或和为 0 0 0,故可以将 a i a_i ai修改为 s u m ⊕ a i sum\oplus a_i sumai,因此我们只需找到第一个 a i ≥ s u m ⊕ a i a_i\ge sum\oplus a_i aisumai即可。

#include 
#define N 500050
using namespace std;
inline void read(int &x){
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+(ch&15);ch=getchar();}
    x=s*w;
}
int n,x,sum,a[N];
int main(){
    read(n);
    for(int i=1;i<=n;i++)read(a[i]),sum^=a[i];
    if(sum==0){
        puts("lose");
        return 0;
    }
    for(int i=1;i<=n;i++){
        if((sum^a[i])<=a[i]){
            printf("%d %d\n",a[i]-(sum^a[i]),i);
            a[i]=(sum^a[i]);
            for(int j=1;j<=n;j++){
                printf("%d",a[j]);
                if(j==n)puts("");
                else putchar(' '); 
            }
            break;
        }
    }
}

P2252 [SHOI2002] 取石子游戏|【模板】威佐夫博弈

题面
威佐夫博弈的结论是,若较大数与较小数的差值与黄金分割比的向下取整为较小数,则该状态为先手必败,证明在第一个题解中有,这里不多赘述。

#include 
#define N 500050
using namespace std;
inline void read(int &x){
    int s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=(s<<3)+(s<<1)+(ch&15);ch=getchar();}
    x=s*w;
}
int n,m;
double g=(sqrt(5.0)+1.0)/2.0;
int main(){
    read(n),read(m);
    if(n<m)swap(n,m);
    int a=n-m;
    if(m==int(a*g))puts("0");
    else puts("1");
}

你可能感兴趣的:(算法,c++)