组合游戏简单题



UVA - 11859 - Division Game

题目传送:Division Game

AC代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;

const int maxn = 10005;

int n, m, t;

int match[55];//把一行的若干个数都变为他们的真因子,即拿掉了一些素因子,即可以看成一堆火柴中取出几根来,所以可以看做Nim类组合游戏

int chu[maxn];
int to[maxn];//计算出每个数字的素因子个数

void init() {//打表计算素因子
    to[1] = 0;
    for(int i = 2; i < maxn; i ++) chu[i] = i;

    for(int i = 2; i < maxn; i ++) {
        if(to[i]) continue;
        to[i] ++;
        for(int j = 2 * i; j < maxn; j += i) {
            while(chu[j] % i == 0) {
                to[j] ++;
                chu[j] /= i;
            }
        }
    }
}


int main() {
    init();
    int T;
    scanf("%d", &T);
    for(int cas = 1; cas <= T; cas ++) {

        memset(match, 0, sizeof(match));

        scanf("%d %d", &n, &m);
        for(int i = 1; i <= n; i ++) {
            for(int j = 1; j <= m; j ++) {
                scanf("%d", &t);
                match[i] += to[t];
            }
        }

        //因为题目可以看做Nim游戏,所以sg数值即为火柴数值的异或和
        int sg = match[1];
        for(int i = 2; i <= n; i ++) {
            sg = sg ^ match[i];
        }

        if(sg == 0) printf("Case #%d: NO\n", cas);//sg为0时先手必败,反之必胜
        else printf("Case #%d: YES\n", cas);

    }
    return 0;
}



UVALive - 5059 - Playing With Stones

题目传送:Playing With Stones

此题可以先打表来找规律。

打表代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;

const int maxn = 100;
int SG[maxn];
int vis[maxn];

int main() {
    SG[1] = 0;//只有一个的时候不能拿,为必败状态
    for(int i = 2; i <= 30; i ++) {
        memset(vis, 0, sizeof(vis));
        for(int j = 1; j * 2 <= i; j ++) vis[SG[i-j]] = 1;
        for(int j = 0;; j++) if(!vis[j]) {
            SG[i]  = j;
            break;
        }
        printf("%d ", SG[i]);
    }
    return 0;
}

规律为:
n为偶数时,SG(n) = n / 2;
n为奇数时,SG(n) = SG(n/2);

AC代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;

LL SG(LL x) {
    return (x & 1) ? SG(x / 2) : x / 2;
}

int main() {
    int T;
    cin >> T;
    while(T --) {
        int n;
        LL a, ans = 0;
        cin >> n;
        for(int i = 0; i < n; i ++) {
            cin >> a;
            ans ^= SG(a);
        }   

        if(ans) cout << "YES\n";
        else cout << "NO\n";

    }   
    return 0;
}



UVA - 10561 - Treblecross

题目传送:Treblecross

昨晚没睡好,然后着凉了,加上一直头疼,今天脑子炸了。。。炸了一下午。。

简直要死啊。。

AC代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;

int sg[205];//sg[i]表示连续的i个空格子组成的棋盘的SG值
int vis[205];

char str[205];
int jinqu[205];
int len;

int pos[205];
int pos_cnt;

int vec[205];
int cnt;

void get_sg() {
    sg[0] = 0;
    sg[1] = 1;
    sg[2] = 1;
    sg[3] = 1;
    sg[4] = 2;
    sg[5] = 2;
    sg[6] = 0;
    for(int i = 7; i <= 205; i ++) {
        memset(vis, 0, sizeof(vis));
        vis[sg[i - 3]] = 1;
        vis[sg[i - 4]] = 1;
        vis[sg[i - 5]] = 1;
        for(int j = 6; j <= i; j ++) {
            vis[(sg[i - j] ^ sg[j - 5])] = 1;
        }
        for(int j = 0; ; j ++) {
            if(!vis[j]) {
                sg[i] = j;
                break;
            }   
        }
    }
}

int get_SG() {
    int cnt = 0;
    int f = 0;
    for(int i = 0; i < len; i ++) {
        if(!jinqu[i]) {
            f ++;
            if(i == len - 1) {
                vec[cnt ++] = f;
            }
        }
        else if(jinqu[i] && f != 0){
            vec[cnt ++] = f;
            f = 0;
        }
    }

    int SG = 0;
    for(int i = 0; i < cnt; i ++) {
        SG ^= sg[vec[i]];
    }
    return SG;
}

int main() {
    get_sg();

    int T;
    scanf("%d", &T);
    while(T --) {
        scanf("%s", str);

        len = strlen(str);
        int flag = 0;
                pos_cnt = 0;
        for(int i = 0; i < len; i ++) {
            if(i < len - 2 && str[i + 1] == 'X' && str[i + 2] == 'X') {
                flag = 1;
                pos[pos_cnt ++] = i;
            }
            else if(i >= 2 && str[i - 1] == 'X' && str[i - 2] == 'X') {
                flag = 1;   
                pos[pos_cnt ++] = i;
            }
            else if(i >= 1 && str[i - 1] == 'X' && str[i + 1] == 'X') {
                flag = 1;
                pos[pos_cnt ++] = i;
            }
        }

        if(flag == 1) {
            printf("WINNING\n");
            for(int i = 0; i < pos_cnt - 1; i ++) {
                printf("%d ", pos[i] + 1);
            }
            printf("%d\n", pos[pos_cnt - 1] + 1);
            continue;
        }

        memset(jinqu, 0, sizeof(jinqu));
        for(int i = 0; i < len; i ++) {
            if(str[i] == 'X') {
                if(i - 2 >= 0) jinqu[i - 2] ++;
                if(i - 1 >= 0) jinqu[i - 1] ++;
                jinqu[i] ++;
                if(i + 1 < len) jinqu[i + 1] ++;
                if(i + 2 < len) jinqu[i + 2] ++;
            }
        }

        int SG = get_SG();

        //cout << SG << endl;

        if(SG == 0) {
            printf("LOSING\n\n");
        }
        else {
            printf("WINNING\n");

            int ans[205];
            int ans_cnt = 0;
            for(int i = 0; i < len ; i++) {
                if(!jinqu[i]) {
                    if(i - 2 >= 0) jinqu[i - 2] ++;
                    if(i - 1 >= 0) jinqu[i - 1] ++;
                    jinqu[i] ++;
                    if(i + 1 < len) jinqu[i + 1] ++;
                    if(i + 2 < len) jinqu[i + 2] ++;
                    int tmp = get_SG();
                    //cout << tmp << " ";
                    if(tmp == 0) {
                        ans[ans_cnt ++] = i + 1;
                        //cout << "dui" << endl;
                    }
                    if(i - 2 >= 0) jinqu[i - 2] --;
                    if(i - 1 >= 0) jinqu[i - 1] --;
                    jinqu[i] --;
                    if(i + 1 < len) jinqu[i + 1] --;
                    if(i + 2 < len) jinqu[i + 2] --;
                }
            }
            //cout << endl;
            for(int i = 0; i < ans_cnt - 1; i ++) {
                printf("%d ", ans[i]);
            }
            printf("%d\n", ans[ans_cnt - 1]);
        }

    }

    return 0;
}



HDU - 2188 - 悼念512汶川大地震遇难同胞——选拔志愿者

题目传送:悼念512汶川大地震遇难同胞——选拔志愿者

简单的威佐夫博奕

当n%(m+1) == 0 时为必败状态

AC代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;

int n, m;

int main() {
    int C;
    scanf("%d", &C);
    while(C --) {
        scanf("%d %d", &n, &m);
        if(m >= n) {
            printf("Grass\n");
        }
        if(n % (m + 1) == 0) {
            printf("Rabbit\n");
        }
        else printf("Grass\n");
    }
    return 0;
}



HDU - 2149 - Public Sale

题目传送:Public Sale

也是威佐夫博奕,只不过要输出必胜走法

AC代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;

int n, m;

int main() {
    while(scanf("%d %d", &n, &m) != EOF) {
        if(m >= n) {
            for(int i = n; i < m; i ++) {
                printf("%d ", i);
            }
            printf("%d\n", m);
        }
        else {
            if(n % (m + 1) == 0) {
                printf("none\n");
            }
            else {
                printf("%d\n", n % (m + 1));
            }
        }
    }
    return 0;
}



HDU - 1850 - Being a Good Boy in Spring Festival

题目传送:Being a Good Boy in Spring Festival

简单Nim博弈,输出可行次数

AC代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;

int a[105];

int n, m;

int main() {
    while(scanf("%d", &m) != EOF) {
        if(m == 0) break;

        int xor_sum = 0;
        for(int i = 0; i < m; i ++) {
            scanf("%d", &a[i]);
            xor_sum ^= a[i];
        }

        if(xor_sum == 0) {
            printf("0\n");
            continue;
        }

        int ans = 0;
        for(int i = 0; i < m; i ++) {
            if(a[i] > (xor_sum^a[i])) { //为了使得下一个状态的sg为0,即必败状态,只需要当前第i个数减去若干数以后可以达到其他数的异或和即可,因为这样减去之后此时的sg值为0
                ans ++;
            }
        }

        printf("%d\n", ans);
    }
    return 0;
}



HDU - 2176 - 取(m堆)石子游戏

题目传送:取(m堆)石子游戏

和上题类似,也是Nim博弈,只不过输出选择的那个罢了

AC代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;

int m;
int a[200005];

int main() {
    while(scanf("%d", &m) != EOF) {
        if(m == 0) break;

        int xor_sum = 0;
        for(int i = 0; i < m; i ++) {
            scanf("%d", &a[i]);
            xor_sum ^= a[i];
        }

        if(xor_sum == 0) {
            printf("No\n");
            continue;
        }

        printf("Yes\n");
        for(int i = 0 ; i < m; i ++) {
            if(a[i] > (xor_sum^a[i])) {
                printf("%d %d\n", a[i], xor_sum^a[i]);
            }
        }
    }
    return 0;
}



UVA - 12293 - Box Game

2015-10-4 更新

啊啊啊啊啊啊。

无聊的国庆节啊,在实验室无聊好久了,刷个水题。。。

这个题可以直接根据sg函数找规律即可,规律为2^x-1的时候输出Bob,否则输出Alice。

而sg函数为sg[n] = mex{sg[n-1], sg[n-2], ……,sg[n/2 +1]}。如果n为偶数要集合里要加上sg[n/2]。

AC代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;

int sg[1005];
int vis[1005];

int n;
set<int> s;

void test() {//通过sg函数找规律
    sg[1] = 0;
    sg[2] = 1;
    for(int i = 3; i <= 80; i ++) {
        memset(vis, 0, sizeof(vis));
        int k;
        if(i & 1) k = i / 2 + 1;
        else k = i / 2;
        for(; k < i; k ++) {
            vis[sg[k]] = 1;
        }
        for(int j = 0; j < 1005; j ++) {
            if(!vis[j]) {
                sg[i] = j;
                break;
            }
        }
    }
    //for(int i = 1; i <= 80; i ++) cout << sg[i] << " "; cout << endl;
    for(int i = 1; i <= 80; i ++) if(sg[i] == 0) cout << i << " "; cout << endl;
}

void init() {
    int jishu = 1;
    for(int i = 1; i <= 30; i ++) {
        jishu *= 2;
        //cout << jishu - 1 << endl;
        s.insert(jishu - 1);
    }
}

int main() {
    init();
    while(scanf("%d", &n) != EOF) {
        if(n == 0) break;
        if(s.find(n) != s.end()) printf("Bob\n");
        else printf("Alice\n");
    }
    return 0;
}



ENimEN

题目传送:UVA - 11892 - ENimEN

分类:逻辑推理

纸上模拟几下可以发现一个规律:

  1. 当n个数中只有1的时候
    • 1的个数为奇数时先手必胜
    • 1的个数为偶数时先手必败
  2. 当n个数中有除了1以外的数时先手必胜

当然,规律1很好理解。

对于规律2,因为此时的状态都可以转化为1的个数为偶数的后继状态。

比如n为5,a={1,1,5,5,5}。先对其中两个5取走4个,再在第三个5中全部取走就变成两个1了,即1的个数为偶数的后继状态,此时先手(初始时的后手)必败。

在比如n为5,a={1,1,1,5,5}。对其中两个5取走4个,先手再取走一个1,则此时也变为1的个数为偶数的后继状态了,此时先手(初始时的后手)必败。

综上,此时原先手必胜。

AC代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;

int n;

int main() {
    int T;
    scanf("%d", &T);
    while(T --) {
        scanf("%d", &n);
        int cnt1 = 0;//用于统计1的个数
        int flag = 0;//用于判断是否有除了1以外的数
        for(int i = 0; i < n; i ++) {
            int t;
            scanf("%d", &t);
            if(t == 1) cnt1 ++;
            else flag = 1;
        }
        if(flag == 1 || cnt1 & 1) printf("poopi\n");
        else printf("piloop\n");
    }
    return 0;
}



A Funny Stone Game

题目传送:UVALive - 3668 - A Funny Stone Game

分析:

一个很经典的博弈题目。关键看怎么分解为子游戏。

这里因为每一堆石子相互之间可能是有影响的,所以不能直接拆分为子游戏。

但是,换个方式思考,就会发现,可以把每一个石子当成一个子游戏。

即可以把位置为i的石子看成一堆个数为n-1-i的石子堆。

所以每一次操作,都会把一堆石子拆分为两个规模更小的石子堆。

比如说,n=3,数组a={1, 2, 3}。此时相当于1堆含有2个石子的石子堆,2堆含有1个石子的石子堆,3个含有0个石子的石子堆(即最后一个数)。
此时选择i=1,j=2,k=3。则等价于将一堆含有2个石子的石子堆->一堆含有1个石子的石子堆,和一堆含有0个石子的石子堆。

好了,总上所述,只要求出23以内的sg函数即可,sg函数可以递推求得,然后暴力找最小字典序解。

AC代码:

#include <map>
#include <set>
#include <list>
#include <cmath>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <cctype>
#include <cstdio>
#include <string>
#include <vector>
#include <complex>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <sstream>
#include <utility>
#include <iostream>
#include <algorithm>
#include <functional>
#define LL long long
#define INF 0x7fffffff
using namespace std;

int n;
int sg[50];

int vis[305];

void get_sg() {
    for(int i = 0; i <= 23; i ++) {
        memset(vis, 0, sizeof(vis));
        for(int j = i - 1; j >= 0; j --) {
            for(int k = j; k >= 0; k --) {
                vis[sg[j] ^ sg[k]] = 1;
            }
        }
        for(int j = 0; ; j ++) {
            if(!vis[j]) {
                sg[i] = j;
                break;
            }
        }
    }
}

int a[50];
int ans;

void get_ans() {
    for(int i = 0; i < n; i ++) {
        if(a[i] == 0) continue;//这里输出的时候,需要特判一下刚开始是否有石子,坑了劳资我好久。。
        for(int j = i + 1; j < n; j ++) {
            for(int k = j; k < n; k ++) {
                if((sg[n - 1 - i] ^ sg[n - 1 - j] ^ sg[n - 1 - k] ^ ans) == 0) {
                    printf("%d %d %d\n", i, j, k);
                    return;
                }
            }
        }
    }
}

int main() {
    get_sg();
    //for(int i = 0; i < 23; i ++) cout << sg[i] << " "; cout << endl;
    int cas = 1;
    while(scanf("%d", &n) != EOF) {
        if(n == 0) break;
        for(int i = 0; i < n; i ++) {
            scanf("%d", &a[i]);
        }
        ans = 0;
        for(int i = 0; i < n; i ++) {
            ans ^= (sg[n - 1 - i] * (a[i] & 1));
        }
        printf("Game %d: ", cas ++);
        if(ans == 0) {
            printf("-1 -1 -1\n");
        }
        else {
            get_ans();
        }
    }
    return 0;
}



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值