zoukankan      html  css  js  c++  java
  • 博弈题目小结

    HDU 2174 kiki's game

    题意:有一个N*M的棋盘,起点在右上角,两个人每轮可把棋子向左、向下或者向左下移动一格,直到不能移动棋子者输。

    NP图解决:

    概念:

    必败点(P点):前一个选手将取胜的位置称为必败点。

    必胜点(N点):下一个选手将取胜的位置成为必胜点。

    性质:

    步骤:

     NP图:

    AC code:

     1 #include <bits/stdc++.h>
     2 #define inc(i, j, k) for(int i = j; i <= k; i++)
     3 #define rep(i, j, k) for(int i = j; i < k; i++)
     4 #define F(x) ((x)/3+((x)%3==1?0:tb))
     5 #define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
     6 #define INF 0x3f3f3f3f
     7 #define LL long long
     8 #define MEM(i, j) memset(i, j, sizeof(i));
     9 #define gcd(i, j) __gcd(i, j)
    10 using namespace std;
    11 
    12 int main()
    13 {
    14     int a, b;
    15     while(~scanf("%d %d", &a, &b)){
    16         if(a == 0 && b == 0) break;
    17         if(a%2 == 1 && b%2 == 1) puts("What a pity!");
    18         else puts("Wonderful!");
    19     }
    20     return 0;
    21 }
    View Code

     HDU 2149  Public Sale

    题意:

    底价为 0,最低成交价为 M,两位博弈,每轮叫价区间 1~N

    求 先手开始有多少种取胜叫价,输出每种叫价值。

    解题思路:

    巴什博弈变形,只要能把 (N+1) * t 留给对手必胜,如果自己是(N+1)*r 必败。

    AC code:

     1 #include <bits/stdc++.h>
     2 #define inc(i, j, k) for(int i = j; i <= k; i++)
     3 #define rep(i, j, k) for(int i = j; i < k; i++)
     4 #define F(x) ((x)/3+((x)%3==1?0:tb))
     5 #define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
     6 #define INF 0x3f3f3f3f
     7 #define LL long long
     8 #define MEM(i, j) memset(i, j, sizeof(i));
     9 #define gcd(i, j) __gcd(i, j)
    10 using namespace std;
    11 
    12 int main()
    13 {
    14     int N, M;
    15     while(~scanf("%d %d", &M, &N)){
    16         if(M <= N){
    17             for(int i = M; i <= N; i++){
    18                 printf("%d%c", i, i==N?'
    ':' ');
    19             }
    20         }
    21         else if(M%(N+1) == 0) puts("none");
    22         else{
    23             printf("%d
    ", M%(N+1));
    24         }
    25     }
    26     return 0;
    27 }
    View Code

    HDU 1907 John

    题意:

    有 N 堆物品,每轮选着一堆拿走若干物品,拿走最后一个物品的输;

    解题思路:

    Nim博弈变形,异或判断是否为奇异局势, 面对奇异局势先手必败,如果全是 1 判断奇偶性,偶数则先手胜。

    AC code:

     1 #include <bits/stdc++.h>
     2 #define inc(i, j, k) for(int i = j; i <= k; i++)
     3 #define rep(i, j, k) for(int i = j; i < k; i++)
     4 #define F(x) ((x)/3+((x)%3==1?0:tb))
     5 #define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
     6 #define INF 0x3f3f3f3f
     7 #define LL long long
     8 #define MEM(i, j) memset(i, j, sizeof(i));
     9 #define gcd(i, j) __gcd(i, j)
    10 using namespace std;
    11 char ans1[] = "John", ans2[] = "Brother";
    12 int main()
    13 {
    14     int N, sum;
    15     bool flag = false;
    16     int T_case, tp;
    17     scanf("%d", &T_case);
    18     while(T_case--){
    19         flag = false;
    20         scanf("%d", &N);scanf("%d", &sum);
    21         if(sum > 1) flag = true;
    22         inc(i, 2, N){
    23             scanf("%d", &tp);
    24             if(tp > 1) flag = true;
    25             sum^=tp;
    26         }
    27         if(!flag) printf("%s", N%2?ans2:ans1);
    28         else printf("%s", sum==0?ans2:ans1);
    29         puts("");
    30     }
    31     return 0;
    32 }
    View Code

    HDU 2509 Be the Winner

    题意:同上;

    解题思路:同上;

    HDU 1850 Being a Good Boy in Spring Festival

    题意:有 N 堆 扑克牌,每轮选择其中一堆拿走任意张牌,拿走最后一张牌的胜,问先手如果想取胜,第一步有多少种选择?

    解题思路:

    Nim博弈变形;

    原理、方法都很详细:https://www.cnblogs.com/kuangbin/archive/2011/11/24/2262389.html

    即枚举每一堆是否可以删掉一些值维持最后 NIM 游戏的状态, NIM 游戏的最后状态是唯一不变的。

    AC code:

     1 #include <bits/stdc++.h>
     2 #define inc(i, j, k) for(int i = j; i <= k; i++)
     3 #define rep(i, j, k) for(int i = j; i < k; i++)
     4 #define F(x) ((x)/3+((x)%3==1?0:tb))
     5 #define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
     6 #define INF 0x3f3f3f3f
     7 #define LL long long
     8 #define MEM(i, j) memset(i, j, sizeof(i));
     9 #define gcd(i, j) __gcd(i, j)
    10 using namespace std;
    11 const int MAXN = 2e5+10;
    12 int tp[MAXN];
    13 int main()
    14 {
    15     int N, sum = 0;
    16     while(~scanf("%d", &N) && N){
    17         int ans = 0;
    18         scanf("%d", &sum);
    19         tp[1] = sum;
    20         inc(i, 2, N){
    21             scanf("%d", &tp[i]);
    22             sum^=tp[i];
    23         }
    24         inc(i, 1, N){
    25             if(tp[i] > (sum^tp[i])) ans++;
    26         }
    27         printf("%d
    ", ans);
    28     }
    29     return 0;
    30 }
    View Code

    HDU 1536 S-Nim

    题意:

    给出一个集合 S 表示游戏中可选的数目,接下来给定 M 个游戏局面即 N 堆物品的大小。两人轮流选择一堆取数 只能取 ∈S 的数目,取走最后一个胜。判断游戏局面是先手胜还是后手胜。

    题目给出了SG函数的用途和求法。

    解题思路:

    SG函数模板。

    AC code:

     1 #include <bits/stdc++.h>
     2 #define inc(i, j, k) for(int i = j; i <= k; i++)
     3 #define rep(i, j, k) for(int i = j; i < k; i++)
     4 #define F(x) ((x)/3+((x)%3==1?0:tb))
     5 #define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
     6 #define INF 0x3f3f3f3f
     7 #define LL long long
     8 #define MEM(i, j) memset(i, j, sizeof(i));
     9 #define gcd(i, j) __gcd(i, j)
    10 using namespace std;
    11 const int MAXN = 2e3+10;
    12 const int MM = 1e4+5;
    13 int S[MAXN], sg[MM];
    14 int vis[MM];
    15 string ans;
    16 
    17 void getsg(int n)
    18 {
    19     sg[0] = 0;
    20     memset(vis, -1, sizeof(vis));
    21     inc(i, 1, MM){
    22         inc(j, 1, n){
    23             if(S[j] > i) break;
    24             vis[sg[i-S[j]]] = i;
    25         }
    26 //        puts("zjj");
    27         int k = 0;
    28         while(vis[k] == i) k++;
    29         sg[i] = k;
    30     }
    31 }
    32 
    33 int main()
    34 {
    35     int N, M, K, tp, res;
    36 
    37     while(~scanf("%d", &N) && N){
    38         ans = "";
    39         inc(i, 1, N) scanf("%d", &S[i]);
    40         //inc(i, 1, N) printf("%d ", S[i]);
    41         sort(S+1, S+N+1);
    42         getsg(N);
    43 //        puts("zjj");
    44         scanf("%d", &K);
    45         while(K--){
    46             scanf("%d", &M);
    47 //            scanf("%d", &res);
    48             res = 0;
    49             inc(i, 1, M){
    50                 scanf("%d", &tp);
    51                 res^=sg[tp];
    52             }
    53             if(res == 0) ans+='L';
    54             else ans+='W';
    55         }
    56         cout << ans << endl;
    57     }
    58 
    59     return 0;
    60 }
    View Code

    HDU 1849 Rabbit and Grass

    题意:一维棋盘,给出每个棋子的初始位置,两人轮流选择一个棋子走到左边任意点。

    解题思路:NIM游戏,把棋子移动看成取石子。

    AC code:

     1 #include <bits/stdc++.h>
     2 #define inc(i, j, k) for(int i = j; i <= k; i++)
     3 #define rep(i, j, k) for(int i = j; i < k; i++)
     4 #define F(x) ((x)/3+((x)%3==1?0:tb))
     5 #define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
     6 #define INF 0x3f3f3f3f
     7 #define LL long long
     8 #define MEM(i, j) memset(i, j, sizeof(i));
     9 #define gcd(i, j) __gcd(i, j)
    10 using namespace std;
    11 
    12 int main()
    13 {
    14     int N, ans, tp;
    15     while(~scanf("%d", &N) && N){
    16         ans = 0;
    17         inc(i, 1, N){
    18             scanf("%d", &tp);
    19             ans^=tp;
    20         }
    21         if(!ans) puts("Grass Win!");
    22         else puts("Rabbit Win!");
    23     }
    24     return 0;
    25 }
    View Code

    HDU 1851 A Simple Game

    题意:N堆物品,每堆每次最多取L个,每人轮流取东西,取走最后那个的获胜。

    解题思路: NIM游戏,每一堆单独作为一个NIM游戏 M%L若为0 先手输,否则后手输, 因为双方都采取最佳策略,所以最后把每一堆的结果异或起来即可。

    AC code:

     1 #include <bits/stdc++.h>
     2 #define inc(i, j, k) for(int i = j; i <= k; i++)
     3 #define rep(i, j, k) for(int i = j; i < k; i++)
     4 #define F(x) ((x)/3+((x)%3==1?0:tb))
     5 #define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
     6 #define INF 0x3f3f3f3f
     7 #define LL long long
     8 #define MEM(i, j) memset(i, j, sizeof(i));
     9 #define gcd(i, j) __gcd(i, j)
    10 using namespace std;
    11 
    12 int main()
    13 {
    14     int N, ans, tp;
    15     while(~scanf("%d", &N) && N){
    16         ans = 0;
    17         inc(i, 1, N){
    18             scanf("%d", &tp);
    19             ans^=tp;
    20         }
    21         if(!ans) puts("Grass Win!");
    22         else puts("Rabbit Win!");
    23     }
    24     return 0;
    25 }
    View Code

    HDU 2897 邂逅明下

    题意:一堆大小为 N 的物品,两人轮流取物品,取值范围 [ p, q ], 若堆物品的数量小于等于 p 则该轮需要全部取走,取走最后物品的败,先手是否能取胜。

    解题思路:Bash博弈变形 如果当前 N == (p+q)*r 先手必胜,因为 先手先取 q 个,接下来每次后手取 k 个,先手取(p+q)-k , 最后剩下一个 p 给后手 ;

                 如果当前 N == (p+q)*r + res,  若  p < res 则 先手胜,先手一开始取 res-p 个, 接下来后手每次取 k 个,先手取 (p+q)-k 个,最后留给后手的肯定 等于 p;

                              若 res < p 则后手胜, 因为无论 先手取 k ,后手取 (p+q)-k ,最后把 res 留给先手。

    AC code:

     1 #include <bits/stdc++.h>
     2 #define inc(i, j, k) for(int i = j; i <= k; i++)
     3 #define rep(i, j, k) for(int i = j; i < k; i++)
     4 #define F(x) ((x)/3+((x)%3==1?0:tb))
     5 #define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
     6 #define INF 0x3f3f3f3f
     7 #define LL long long
     8 #define MEM(i, j) memset(i, j, sizeof(i));
     9 #define gcd(i, j) __gcd(i, j)
    10 using namespace std;
    11 
    12 int main()
    13 {
    14     int N, p, q;
    15     while(~scanf("%d %d %d", &N, &p, &q)){
    16         int res = N%(p+q);
    17         if(res == 0 || (res > p && res < p+q)) puts("WIN");
    18         else puts("LOST");
    19     }
    20     return 0;
    21 }
    View Code

    HDU 2516 取石子游戏

    题意:有一堆大小为 N 的石子,第一轮可以取任意数量的石子,但不能全取,接下来每一轮只能取不超过上一轮取数得两倍的石子,没有石子取得输。

    解题思路:

    斐波那契博弈(Fibonacci Nim)

    有一堆个数为n(n>=2)的石子,游戏双方轮流取石子,规则如下:

    1)先手不能在第一次把所有的石子取完,至少取1颗;

    2)之后每次可以取的石子数至少为1,至多为对手刚取的石子数的2倍。

    约定取走最后一个石子的人为赢家,求必败态。

    结论:当n为Fibonacci数的时候,必败。

    f[i]:1,2,3,5,8,13,21,34,55,89……

    AC code:

     1 #include <bits/stdc++.h>
     2 #define inc(i, j, k) for(int i = j; i <= k; i++)
     3 #define rep(i, j, k) for(int i = j; i < k; i++)
     4 #define F(x) ((x)/3+((x)%3==1?0:tb))
     5 #define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
     6 #define INF 0x3f3f3f3f
     7 #define LL long long
     8 #define MEM(i, j) memset(i, j, sizeof(i));
     9 #define gcd(i, j) __gcd(i, j)
    10 using namespace std;
    11 const int MAXN = 50;
    12 LL m[MAXN];
    13 int main()
    14 {
    15     m[0] = 0;
    16     m[1] = 1;
    17     m[2] = 2;
    18     for(int i = 3; i < MAXN; i++){
    19         m[i] = m[i-1]+m[i-2];
    20 //        cout << m[i] << endl;
    21     }
    22 //    printf("%d
    ", m[MAXN-1]);
    23     LL N;
    24     while(~scanf("%lld", &N) && N){
    25         bool flag = true;
    26         for(int i = 0; i < MAXN; i++){
    27             if(m[i] == N){
    28                 flag = false;
    29             }
    30         }
    31         if(flag) puts("First win");
    32         else puts("Second win");
    33 
    34     }
    35     return 0;
    36 }
    View Code

    HDU 1847 Good Luck in CET-4 Everybody!

    题意:N 堆物品,两人轮流取,只能取 2的幂次倍, 取走最后一个的胜。

    解题思路: NP分析,必败点为 0,从必败点加任意一个二次幂都是必胜点,即必胜点一定可以删掉一个二次幂变成一个必败点。

          所以这题类似于素数筛,从小的 P点 开始筛 N 点,遍历过去如果遇到未被前面筛掉的点必定为 P 点(因为未被前面的 P 点筛到,说明当前点的值由两个 N 点组成,所以无论 当前点如何作差最后都是变成 一个 N 点,后手必胜),所以直接筛过去是合理的。

    AC code:

     1 #include <set>
     2 #include <map>
     3 #include <queue>
     4 #include <cmath>
     5 #include <cstdio>
     6 #include <string>
     7 #include <vector>
     8 #include <cstdlib>
     9 #include <cstring>
    10 #include <iostream>
    11 #include <algorithm>
    12 #define inc(i, j, k) for(int i = j; i <= k; i++)
    13 #define rep(i, j, k) for(int i = j; i < k; i++)
    14 #define F(x) ((x)/3+((x)%3==1?0:tb))
    15 #define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
    16 #define INF 0x3f3f3f3f
    17 #define LL long long
    18 #define MEM(i, j) memset(i, j, sizeof(i));
    19 #define gcd(i, j) __gcd(i, j)
    20 using namespace std;
    21 const int MAXN = 1e3+10;
    22 bool f[MAXN];
    23 void init()
    24 {
    25     MEM(f, 0);
    26     rep(i, 0, MAXN){
    27         if(!f[i]){
    28             int tp = 1;
    29             while(i+tp < MAXN){
    30                 f[i+tp] = true;
    31                 tp<<=1;
    32             }
    33         }
    34     }
    35 }
    36 
    37 int main()
    38 {
    39     int N;
    40     init();
    41     while(~scanf("%d", &N)){
    42         if(f[N]) puts("Kiki");
    43         else puts("Cici");
    44     }
    45     return 0;
    46 }
    View Code

    HDU 1079 Calendar Game

    题意:给定一个起始日期,可以跳到下个月的这一天或者下一天,如果下个月没有这一天就只能跳到下一天。最后恰好跳到 2001/11/04 的人获胜,超过这个日期的会失败。

    解题思路:NP分析,可以直接找规律,也可以直接记忆化搜索转移状态。

    AC code:

      1 #include <set>
      2 #include <map>
      3 #include <queue>
      4 #include <cmath>
      5 #include <cstdio>
      6 #include <string>
      7 #include <vector>
      8 #include <cstdlib>
      9 #include <cstring>
     10 #include <iostream>
     11 #include <algorithm>
     12 #define inc(i, j, k) for(int i = j; i <= k; i++)
     13 #define rep(i, j, k) for(int i = j; i < k; i++)
     14 #define F(x) ((x)/3+((x)%3==1?0:tb))
     15 #define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
     16 #define INF 0x3f3f3f3f
     17 #define LL long long
     18 #define MEM(i, j) memset(i, j, sizeof(i));
     19 #define gcd(i, j) __gcd(i, j)
     20 using namespace std;
     21 const int MAXN = 2e3+10;
     22 int dat[MAXN][50][50];
     23 int da[] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
     24 bool check(int yy, int mm, int dd)
     25 {
     26     if(mm > 12) return false;
     27     if(((yy%4==0 && yy%100!=0) || yy%400==0) && mm == 2 && dd <= 29) return true;
     28     if(da[mm] < dd) return false;
     29     return true;
     30 }
     31 
     32 int solve(int yy, int mm, int dd)
     33 {
     34     if(dat[yy][mm][dd] != -1) return dat[yy][mm][dd];
     35     if(yy > 2001) return dat[yy][mm][dd] = 1;
     36     if(yy == 2001 && mm > 11) return dat[yy][mm][dd] = 1;
     37     if(yy == 2001 && mm == 11 && dd > 4) return dat[yy][mm][dd] = 1;
     38     if(yy == 2001 && mm == 11 && dd == 4) return dat[yy][mm][dd] = 0;
     39 
     40     int ty = yy, tm = mm, td = dd;
     41     td++;
     42     if(((yy%4==0 && yy%100!=0) || yy%400==0) && tm == 2){
     43         if(td > 29){
     44             td = 1;
     45             tm++;
     46         }
     47         if(solve(ty, tm, td) == 0) return dat[yy][mm][dd] = 1;
     48     }
     49     else{
     50         if(da[tm] < td){
     51             td = 1;
     52             tm++;
     53         }
     54         if(tm > 12) tm = 1, ty++;
     55         if(solve(ty, tm, td) == 0) return dat[yy][mm][dd] = 1;
     56     }
     57 
     58     ty = yy, tm = mm, td = dd;
     59     tm++;
     60     if(tm > 12) ty++, tm = 1;
     61     if(check(ty, tm, td) && solve(ty, tm, td) == 0) return dat[yy][mm][dd] = 1;
     62 
     63     return dat[yy][mm][dd] = 0;
     64 
     65 }
     66 
     67 int main()
     68 {
     69     MEM(dat, -1);
     70     int T_case, y, m, d;
     71     scanf("%d", &T_case);
     72     while(T_case--){
     73         scanf("%d %d %d", &y, &m, &d);
     74         if(solve(y, m, d)) puts("YES");
     75         else puts("NO");
     76     }
     77     return 0;
     78 }
     79 
     80 // (天数+日数)偶数是真,奇数是假,例外:9/30 11/30
     81 //#include <set>
     82 //#include <map>
     83 //#include <queue>
     84 //#include <cmath>
     85 //#include <cstdio>
     86 //#include <string>
     87 //#include <vector>
     88 //#include <cstdlib>
     89 //#include <cstring>
     90 //#include <iostream>
     91 //#include <algorithm>
     92 //#define inc(i, j, k) for(int i = j; i <= k; i++)
     93 //#define rep(i, j, k) for(int i = j; i < k; i++)
     94 //#define F(x) ((x)/3+((x)%3==1?0:tb))
     95 //#define G(x) ((x)<tb?(x)*3+1:((x)-tb)*3+2)
     96 //#define INF 0x3f3f3f3f
     97 //#define LL long long
     98 //#define MEM(i, j) memset(i, j, sizeof(i));
     99 //#define gcd(i, j) __gcd(i, j)
    100 //using namespace std;
    101 //int main()
    102 //{
    103 //    int T_case;
    104 //    int yy,mm,dd;
    105 //    scanf("%d",&T_case);
    106 //    while(T_case--)
    107 //    {
    108 //        scanf("%d%d%d",&yy,&mm,&dd);
    109 //        if( (dd+mm)%2 == 0 || (mm == 9 && dd == 30) || (mm == 11 && dd == 30) ) puts("YES");
    110 //        else puts("NO");
    111 //    }
    112 //    return 0;
    113 //}
    View Code
  • 相关阅读:
    poj 3243 Clever Y(BabyStep GiantStep)
    poj 2417 Discrete Logging
    poj 3481 Double Queue
    hdu 4046 Panda
    hdu 2896 病毒侵袭
    poj 1442 Black Box
    hdu 2815 Mod Tree
    hdu 3065 病毒侵袭持续中
    hdu 1576 A/B
    所有控件
  • 原文地址:https://www.cnblogs.com/ymzjj/p/10731673.html
Copyright © 2011-2022 走看看