Training:组合博弈入门

来源:互联网 发布:录制绘画过程软件 编辑:程序博客网 时间:2024/06/05 19:14

HDU 1846:
http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=11241
给出n个物品,两个人轮流取,每次取1~m个,不能取的人负,输出胜利者。
巴什博弈,当n%(m+1)=0时后手胜利,否则先手胜利。

#include<cstdio>int main(){    int t;scanf("%d",&t);    while(t--){        int n,m;scanf("%d%d",&n,&m);        puts(n%(m+1)?"first":"second");    }    return 0;}

HDU 1847:
http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=20980
给出n个物品,两个人轮流取,每次只能取2的倍数个。
巴什博弈,第一个先手的人只要能构造出3的倍数就可以必胜。对三取余判断输出结果。

#include<cstdio>int main(){    int n;    while(~scanf("%d",&n))        puts(n%3?"Kiki":"Cici");    return 0;}

HDU 1848:
http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=22378
给出三堆牌,每次去的个数只能是菲波那契数,问谁能赢。
看了解题报告才知道这是运用SG函数的尼姆博弈,去现学的,对每个输入的值求SG函数值,然后进行异或求解。

#include<cstdio>#include<cstring>const int maxn=1010;int f[20]={1,1},sg[maxn];bool h[maxn][20];void pre(){    for(int i=2;i<20;++i) f[i]=f[i-1]+f[i-2];    memset(sg,-1,sizeof(sg));    return;}int Sg(int n){    int& k=sg[n];    if(k!=-1) return k;    for(int i=1;i<20;++i)        if(n-f[i]>=0) h[n][Sg(n-f[i])]=true;    k=0;    while(h[n][k]) ++k;    return k;}int main(){    pre();    int n,m,p;    while(~scanf("%d%d%d",&n,&m,&p)&&!(!n&&!m&&!p))         puts(Sg(n)^Sg(m)^Sg(p)?"Fibo":"Nacci");    return 0;}

HDU 1849:
http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=10636
可以抽象成给出n堆牌,每次可以取一张或者全取走,问谁胜。
比上一题简单的尼姆博弈,对输入的数进行异或输出。

#include<cstdio>int main(){    int n;    while(~scanf("%d",&n)&&n){        int ans=0,k;        while(n--){            scanf("%d",&k);            ans^=k;        }        puts(ans?"Rabbit Win!":"Grass Win!");    }    return 0;}

HDU 1850:
http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=22381
给出多堆牌,每次可以任选一堆取走任意数量,问先手第一步有多少必胜取法。
首先异或判断是否存在必胜策略求的异或后的值为ans。判断当前堆中有没有足够的牌,使得取走之后ans0,若有则方案数加一。

#include<cstdio>int a[110],n;int main(){    while(~scanf("%d",&n)&&n){        int ans=0,cnt=0;        for(int i=0;i<n;++i) scanf("%d",&a[i]),ans^=a[i];        if(!ans){puts("0");continue;}        for(int i=0;i<n;++i)            if(a[i]>(ans^a[i])) ++cnt;        printf("%d\n",cnt);    }    return 0;}

HDU 2149:
http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=10637
输入土地成本m和每次加价最大值n,当出价高于或等于成本时得到该土地,输出先手必胜策略。
巴什博弈,首先判断是否存在必胜策略,然后枚举输出可行出价。

#include<cstdio>int main(){    int n,m;    while(~scanf("%d%d",&n,&m)){        if(n%(m+1)==0){puts("none");continue;}        bool first=true;        if(n<m){            for(int i=n;i<=m;++i){                printf("%d%c",i,i!=m?' ':'\n');            }            continue;        }        for(int i=1;i<=m;++i){            if((n-i)%(m+1)==0){                if(first) first=false;                else printf(" ");                printf("%d",i);            }        }        puts("");    }    return 0;}

HDU 2188:
http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=26664
巴什博弈,和第一道题一样。

#include<cstdio>int main(){    int t,n,m;scanf("%d",&t);    while(t--){        scanf("%d%d",&n,&m);        puts(n%(m+1)?"Grass":"Rabbit");    }    return 0;}

HDU 2147:
http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=26673
给出目标点的坐标,从原点出发,每次可以向左、向下或者向左下,问谁能赢。
当横纵坐标存在偶数时,先手赢。

#include<cstdio>int main(){    int n,m;    while(~scanf("%d%d",&n,&m)&&!(!n&&!m))        puts(n&1&&m&1?"What a pity!":"Wonderful!");    return 0;}

HDU 1079:
http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=11243
改变月份或移到后一天会改变月份与日期和的奇偶性,除了两个特殊日期9月30日和11月30日。对特殊日期进行特判,其他情况只要判断奇偶性就可以知道。
这题还可以用SG函数做。

#include<cstdio>int main(){    int t,y,m,d;scanf("%d",&t);    while(t--&&~scanf("%d%d%d",&y,&m,&d))        puts((m+d)%2==0||((m==9||m==11)&&d==30)?"YES":"NO");    return 0;}

HDU 1517:
http://acm.hust.edu.cn/vjudge/problem/visitOriginUrl.action?id=13014
看了题解才会的,类似于巴什博弈,每一段必胜区间都紧跟着一段必败区间,必胜区间对n不断除18之后的余数小于9。
这道题也可以用SG函数做。

#include<cstdio>int main(){    double n;    while(~scanf("%lf",&n)){        while(n>18) n/=18;        puts(n>9?"Ollie wins.":"Stan wins.");    }    return 0;}
0 0
原创粉丝点击