ACM博弈专题

HDU 3980 Paint Chain 博弈 SG函数

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Problem Description

Aekdycoin and abcdxyzk are playing a game. They get a circle chain with some beads. Initially none of the beads is painted. They take turns to paint the chain. In Each turn one player must paint a unpainted beads. Whoever is unable to paint in his turn lose the game. Aekdycoin will take the first move.

Now, they thought this game is too simple, and they want to change some rules. In each turn one player must select a certain number of consecutive unpainted beads to paint. The other rules is The same as the original. Who will win under the rules ?You may assume that both of them are so clever.

Input

First line contains T, the number of test cases. Following T line contain 2 integer N, M, indicate the chain has N beads, and each turn one player must paint M consecutive beads. (1 <= N, M <= 1000)

Output

For each case, print "Case #idx: " first where idx is the case number start from 1, and the name of the winner.

Sample Input

2
3 1
4 2

Sample Output

Case #1: aekdycoin
Case #2: abcdxyzk

  • 题意:长度为n的环,每次操作取m个连续的块染色,aek…先手,轮流操作,不能继续操作的人输。

  • 分析:长度为n,第一个人涂色之后就把环变成了一个长度为 n-m 的链了,局面由一个n变为 x , m, n - m - x,其中m是被染色完全的,所以变成了两个长度为 x 和 n-x-m 子游戏了。由 SG 定理,SG(n)=SG(x)^SG(n-x-m)。再由 SG 函数的定义式 SG[u]=mex(seg[v])。

  • 代码:

const int SZ=1e3+5;

int sg[SZ];
int n,m;
int mex(int n){

    if (sg[n]!=-1) return sg[n];
    if (n < m) return sg[n] = 0;
    int vis[SZ];
    memset(vis, 0, sizeof vis);
    for (int i = 0; i + m <=n; i++) {
        if (sg[i] == -1) sg[i] = mex(i);
        if (sg[n - i - m] == -1) sg[n - i - m] = mex(n - i - m);
        vis[sg[i] ^ sg[n - i- m]] = 1;
    }
    for (int i = 0;; i++)
    if (vis[i] == 0) {
        sg[n] = i;
        break;
    }
    return sg[n];
}
int main()
{
    int T,cas = 0;
    scanf("%d",&T);
    while (T--) {
        scanf("%d%d",&n,&m);
        printf("Case #%d: ",++cas);
        memset(sg,-1,sizeof sg);
        if (m > n) puts("abcdxyzk");
        else if (m == n) puts("aekdycoin");
        else {
            n -= m;
            puts(mex(n) ? "abcdxyzk" : "aekdycoin");
        }
    }
    return 0;
}

HDU 4778 Gems Fight 博弈 dp状压

Time Limit: 20000/10000 MS (Java/Others) Memory Limit: 327680/327680 K (Java/Others)

Problem Description

Alice and Bob are playing “Gems Fight!”:
  There are Gems of G different colors , packed in B bags. Each bag has several Gems. G different colors are numbered from color 1 to color G.
  Alice and Bob take turns to pick one bag and collect all the Gems inside. A bag cannot be picked twice. The Gems collected are stored in a shared cooker.
  After a player ,we name it as X, put Gems into the cooker, if there are S Gems which are the same color in the cooker, they will be melted into one Magic Stone. This reaction will go on and more than one Magic Stone may be produced, until no S Gems of the same color remained in that cooker. Then X owns those new Magic Stones. When X gets one or more new Magic Stones, he/she will also get a bonus turn. If X gets Magic Stone in a bonus turn, he will get another bonus turn. In short,a player may get multiple bonus turns continuously.
  There will be B turns in total. The goal of “Gems Fight!” is to get as more Magic Stones than the opponent as possible.
  Now Alice gets the first turn, and she wants to know, if both of them act the optimal way, what will be the difference between the number of her Magic Stones and the number of Bob’s Magic Stones at the end of the game.

Input

There are several cases(<=20).
  In each case, there are three integers at the first line: G, B, and S. Their meanings are mentioned above.
  Then B lines follow. Each line describes a bag in the following format:
  
  n c1 c2 … cn
  
  It means that there are n Gems in the bag and their colors are color c1,color c2…and color cn respectively.
   0<=B<=21, 0<=G<=8, 0   There may be extra blank lines between cases. You can get more information from the sample input.
  The input ends with G = 0, B = 0 and S = 0.

Output

One line for each case: the amount of Alice’s Magic stones minus the amount of Bob’s Magic Stones.

Sample Input

3 4 3
2 2 3
2 1 3
2 1 2
3 2 3 1

3 2 2
3 2 3 1
3 1 2 3

0 0 0

Sample Output

3
-3

  • 题意:B个包裹,Alice和Bob依次轮流选,放在一起,每个包裹里面有bi个不同颜色的宝石,当某人新放入一个包裹的宝石的时候,若有同色宝石个数大于等于S,即可合成一个魔法石,同时作为奖励他可以获得额外一次选包裹。问最后Alice比Bob的魔法石多几个?

  • 分析:

    • 定义局面:二进制压缩状态,用0/1表示某个包裹被选了/未被选。
    • 定义状态:dp[i]表示状态为i时,先手的收益 减去 后手的收益。
    • 转移方程与博弈状态(先手\后手):
      转移方程: dp[i] = max(dp[i] , dp[k] + get(i,k))
      k为状态i的前一个状态,即少取一个包裹,get(i,k)为两个状态之间的魔法石个数。
      若get(i,k)为0,则先手后手顺序颠倒,此时
      dp[i] = max(dp[i], - dp[k])
      博弈上理解就是,若没有奖励的额外机会,那么先手变后手,dp[]表示的差值取反就与之对应。
  • 代码:

const int SZ=1e3+5;
int c[22][20];
int dp[1<<22],cooker[20],tmp[20];
int main()
{
    int G,B,S;
    while (scanf("%d%d%d",&G,&B,&S) == 3) {
        if (G + B + S == 0) break;

        for (int i = 0; i < B; i++) {
            int n, x;
            scanf("%d",&n);
            for (int j = 1; j <= n; j++) {
                scanf("%d",&x);
                c[i][x] ++;
            }
        }

        dp[0] = 0;
        int V = (1 << B);
        for (int i = 1; i < V; i++) {
            dp[i] = -INF;
            memset(cooker, 0, sizeof cooker);
            for (int j = 0; j < B; j++)
            if ((i & (1 << j)) == 0) {
                    for (int k = 1; k <= G; k++) {
                        cooker[k] += c[j][k];
                        cooker[k] %= S;
                    }
                //int t = i - (1 << j);
                //dp[i] = max(dp[i], )
            }

            for (int j = 0; j < B; j++) {
                if (i & (1 << j)) {
                    for (int k = 1; k <= G;k++)
                        tmp[k] = cooker[k];
                    int get = 0;
                    for (int k = 1; k <= G;k++) {
                        tmp[k] += c[j][k];
                        get += tmp[k] / S;
                        tmp[k] %= S;
                    }
                    if (get) dp[i] = max(dp[i],dp[i ^ (1 << j)] + get);
                    else dp[i] = max(dp[i], - dp[i ^ (1 << j)]);
                }
            }
        }
        memset(c, 0 ,sizeof c);
        printf("%d\n",dp[V - 1]);
    }
    return 0;
}

HDU 4764 Stone 巴什博弈

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Problem Description

Tang and Jiang are good friends. To decide whose treat it is for dinner, they are playing a game. Specifically, Tang and Jiang will alternatively write numbers (integers) on a white board. Tang writes first, then Jiang, then again Tang, etc… Moreover, assuming that the number written in the previous round is X, the next person who plays should write a number Y such that 1 <= Y - X <= k. The person who writes a number no smaller than N first will lose the game. Note that in the first round, Tang can write a number only within range [1, k] (both inclusive). You can assume that Tang and Jiang will always be playing optimally, as they are both very smart students.

Input

There are multiple test cases. For each test case, there will be one line of input having two integers N (0 < N <= 10^8) and k (0 < k <= 100). Input terminates when both N and k are zero.

Output

For each case, print the winner’s name in a single line.

Sample Input

1 1
30 3
10 2
0 0

Sample Output

Jiang
Tang
Jiang

  • 题意:T和J,轮流写数字,前面一个人写x那么后面一个人写y 要使得1<=y-x<=k. 写的数>=n则输。
  • 巴什博弈:必败点是 (n - 1) % ( k + 1) = 0。因为n - 1状态是必胜态,若(n-1)是(k + 1)的倍数,无论先手是多少,后手都可以取走k + 1剩下部分,最后后手到达(n - 1)必胜态。
  • 巴什博弈百度百科,click here.
  • 代码
int main(){
    int n,k;
    while(scanf("%d%d",&n,&k) == 2){
        if (n + k == 0) break;
        if ((n - 1) % (k + 1) != 0)
            puts("Tang");
        else puts("Jiang");
    }
    return 0;
}

HDU 4778 Mine Nim博弈

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)

Problem Description

Have you ever played a game in Windows: Mine?
This game is played on a n*m board, just like the Pic(1)
On the board, Under some grids there are mines (represent by a red flag). There are numbers ‘A(i,j)’ on some grids means there’re A(i,j) mines on the 8 grids which shares a corner or a line with gird(i,j). Some grids are blank means there’re no mines on the 8 grids which shares a corner or a line with them.
At the beginning, all grids are back upward.
In each turn, Player should choose a back upward grid to click.
If he clicks a mine, Game over.
If he clicks a grid with a number on it , the grid turns over.
If he clicks a blank grid, the grid turns over, then check grids in its 8 directions.If the new grid is a blank gird or a grid with a number,it will be clicked too.
So If we click the grid with a red point in Pic(1), grids in the area be encompassed with green line will turn over.
Now Xiemao and Fanglaoshi invent a new mode of playing Mine. They have found out coordinates of all grids with mine in a game. They also find that in a game there is no grid will turn over twice when click 2 different connected components.(In the Pic(2), grid at (1,1) will turn over twice when player clicks (0,0) and (2,2) , test data will not contain these cases).
Then, starting from Xiemao, they click the grid in turns. They both use the best strategy. Both of them will not click any grids with mine, and the one who have no grid to click is the loser.
Now give you the size of board N, M, number of mines K, and positions of every mine Xi,Yi. Please output who will win.

Input

Multicase
The first line of the date is an integer T, which is the number of the text cases. (T<=50)
Then T cases follow, each case starts with 3 integers N, M, K indicates the size of the board and the number of mines.Then goes K lines, the ith line with 2 integer Xi,Yi means the position of the ith mine.
1<=N,M<=1000 0<=K<=N*M 0<=Xi

Output

For each case, first you should print "Case #x: ", where x indicates the case number between 1 and T . Then output the winner of the game, either ”Xiemao” or “Fanglaoshi”. (without quotes)

Sample Input

2
3 3 0
3 3 1
1 1

Sample Output

Case #1: Xiemao
Case #2: Fanglaoshi

  • 题意:扫雷游戏,已知雷的数量和位置,每人一次要点击一个块,如果有数字就只能点开这一个块,如果没有数字,就可以点开所有空白格和其外层带数字的一圈。没格子可点的会输。

  • 题目分析:首先需要bfs,找出所有空白块以及周围相连带数字的块,以及单个带数字的块,联通的空白块算做一个。之后是博弈的过程,对于有空白块的堆,每次可以去一个或者全取出。一个数字的sg值显然是mex{0} = 1,对于有空白块的,有两种子状态,点一个数字,或者点掉整个块,所以sg值是mex{0,1} = 2 ,mex{0, 2} = 1,打表就是1和2循环的。之后求所有堆的sg值异或和即可。

  • 代码

#include
using namespace std;

typedef long long LL;
const int MAXN=1e3+10;

int n,m,k,tot;

int ans;
int a[MAXN][MAXN],mark[MAXN][MAXN];
int gx[] = {1,1,1,0,0,-1,-1,-1};
int gy[] = {1,-1,0,1,-1,0,1,-1};
void init()
{
    memset(a, 0, sizeof a);
    memset(mark, 0, sizeof mark);
    tot = n * m;
}
int is_zero(int i,int j)
{
    int sum = 0;
    for (int k = 0;k < 8; k++)
    {
        int x = i + gx[k];
        int y = j + gy[k];
        if (x < 0 || y < 0 || x >= n || y >= m) continue;
        sum += a[x][y];
    }
    sum += a[i][j];
    if (sum == 0) return 1;
    else return 0;
}
int bfs(int i,int j)
{
    queue<pair<int,int> > q;
    while (!q.empty()) q.pop();
    int num = 0;
    q.push(make_pair(i, j));
    mark[i][j] = 1;
    tot --;
    while (!q.empty()) {
        int x = q.front().first;
        int y = q.front().second;
        q.pop();

        for (int k = 0;k < 8; k++) {
            int ux = x + gx[k];
            int uy = y + gy[k];
            if (ux < 0 || uy < 0 || ux >= n || uy >= m) continue;
            if (!mark[ux][uy] && !a[ux][uy]) {
                mark[ux][uy] = 1;
                if (is_zero(ux, uy) != 0) {
                q.push(make_pair(ux, uy));
                tot --;
                } else {
                    num++;
                }
            }
        }
    }
    return num;
}
int main(){
    int T,cas = 0;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d%d",&n,&m,&k);
        init();

        for (int i = 0; i < k; i++) {
                int x,y;
            scanf("%d%d",&x,&y);
            a[x][y] = 1;
        }

        tot -= k;
        ans = 0;

        for (int i = 0; i < n; i++)
            for (int j = 0; j < m; j++)
            if (is_zero(i, j) && !mark[i][j]) {
                int t = bfs(i, j);
                ans = ans ^ ((t & 1) + 1);
            }
        ans = ans ^ (tot & 1);
        printf("Case #%d: ",++cas);
        if (ans) puts("Xiemao");
        else puts("Fanglaoshi");
    }
    return 0;
}

HDU 4664 Triangulation Nim博弈

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Problem Description

There are n points in a plane, and they form a convex set.
No, you are wrong. This is not a computational geometry problem.
Carol and Dave are playing a game with this points. (Why not Alice and Bob? Well, perhaps they are bored. ) Starting from no edges, the two players play in turn by drawing one edge in each move. Carol plays first. An edge means a line segment connecting two different points. The edges they draw cannot have common points.
To make this problem a bit easier for some of you, they are simutaneously playing on N planes. In each turn, the player select a plane and makes move in it. If a player cannot move in any of the planes, s/he loses.
Given N and all n’s, determine which player will win.

Input

First line, number of test cases, T.
Following are 2*T lines. For every two lines, the first line is N; the second line contains N numbers, n1, …, nN.

Sum of all N <= 106.
1<=ni<=109.

Output

T lines. If Carol wins the corresponding game, print ‘Carol’ (without quotes;) otherwise, print ‘Dave’ (without quotes.)

Sample Input

2
1
2
2
2 2

Sample Output

Carol
Dave

  • 题意:ni个凸包上的点,每次操作连接两个节点,线段不能相交,N个这样的凸包,不能操作的输。

  • 分析:考虑一个凸包,每次连接两个节点,局面相当于变成了分割的左右两侧,典型的Nim博弈,将每个凸包的sg值异或和就是答案。

  • 做法:0个点和1个点局面显然是输的状态,sg值为0,从2开始算sg值,n个点的局面会变成 i,n - i - 2两个子局面,i 从0 到 n - 2。根据sg公式算即可。但是因为n特别大,打出前300项发现有循环节,数字都是0-9的,直接找9发现循环节,暴力出200个,超出的按循环取余数就可以。

  • 代码:

#include
using namespace std;

typedef long long LL;
const int MAXN=1e3+10;

int sg[MAXN];
int mark[20];
void sgg(int n)
{
    sg[0] = 0;
    sg[1] = 0;
    for (int i = 2; i <= n; i++)
    {
        memset(mark, 0, sizeof mark);
        for (int j = 0; j < i - 1; j++)
            mark[sg[j] ^ sg[i - j - 2]] = 1;
        for (int j = 0;; j++)
        if (mark[j] == 0) {
            sg[i] = j;
            break;
        }
    }
}
int main(){
    int n,k;
    int T;
    scanf("%d",&T);
    sgg(200);
   // for (int i = 1; i <= 200; i++)
      //  printf("%d %d\n",i,sg[i]);
    while(T--){
        scanf("%d",&n);
        int ans = 0, x;
        for (int i = 0; i < n; i++)
            {
                scanf("%d", &x);
                if (x <= 86) ans = ans ^ sg[x];
                else ans = ans ^ sg[(x - 86) % 34 + 86];
            }
        if (ans) puts("Carol");
        else puts("Dave");
    }
    return 0;
}

HDU 4642 Fliping game Nim博弈

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Problem Description

Alice and Bob are playing a kind of special game on an NM board (N rows, M columns). At the beginning, there are NM coins in this board with one in each grid and every coin may be upward or downward freely. Then they take turns to choose a rectangle (x1, y1)-(n, m) (1 ≤ x1≤n, 1≤y1≤m) and flips all the coins (upward to downward, downward to upward) in it (i.e. flip all positions (x, y) where x1≤x≤n, y1≤y≤m)). The only restriction is that the top-left corner (i.e. (x1, y1)) must be changing from upward to downward. The game ends when all coins are downward, and the one who cannot play in his (her) turns loses the game. Here’s the problem: Who will win the game if both use the best strategy? You can assume that Alice always goes first.

Input

The first line of the date is an integer T, which is the number of the text cases.
Then T cases follow, each case starts with two integers N and M indicate the size of the board. Then goes N line, each line with M integers shows the state of each coin, 1<=N,M<=100. 0 means that this coin is downward in the initial, 1 means that this coin is upward in the initial.

Output

For each case, output the winner’s name, either Alice or Bob.

Sample Input

2
2 2
1 1
1 1
3 3
0 0 0
0 0 0
0 0 0

Sample Output

Alice
Bob

  • 题意:给一个矩阵,每个格子有一个硬币,初始状态1代表面朝上,0代表面朝下,两人轮流翻硬币,每次选一个面朝上的硬币为左上角,n,m的硬币为右下角,然后将这个矩形内的硬币都翻转一次,不能再翻转硬币的输。
  • 思路:无论是谁翻硬币,n,m位置的硬币必然会被翻转只需要考虑n,m处的硬币,只要是1,那么必然要无论其他位置怎么翻转,这个位置必须要奇数次才能变成0,所以必然是奇数次,A胜,反之如果是0,必然是偶数次,B胜。若是无法发现这个性质,也可以从n=2 n=3手推一下Nim函数

0 0 0 0 0 0 0 0 0 1 1 1
0 0 0 0 0 0 0 1 1 1 1 1
0 0 0 的局面sg值为0,他的子局面 0 0 1 和 0 1 1 和 1 1 1。这样暴力算所有局面sg值。。然后还是需要找规律发现结论的,不然N = 100,2 100承受不了。

  • 代码:
#include
using namespace std;
int main(){
    int T,x,n,m;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        for (int i = 0; i < n; i++)
            for (int j = 0; j < m; j++)
                scanf("%d", &x);
        if (x) puts("Alice");
        else puts("Bob");
    }
    return 0;
}

HDU 3537 Daizhenyang’s Coin Nim博弈

Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Problem Description

We know that Daizhenyang is chasing a girlfriend. As we all know, whenever you chase a beautiful girl, there’ll always be an opponent, or a rival. In order to take one step ahead in this chasing process, Daizhenyang decided to prove to the girl that he’s better and more intelligent than any other chaser. So he arranged a simple game: Coin Flip Game. He invited the girl to be the judge.
In this game, n coins are set in a row, where n is smaller than 10^8. They took turns to flip coins, to flip one coin from head-up to tail-up or the other way around. Each turn, one can choose 1, 2 or 3 coins to flip, but the rightmost selected must be head-up before flipping operation. If one cannot make such a flip, he lost.
As we all know, Daizhenyang is a very smart guy (He’s famous for his 26 problems and Graph Theory Unified Theory-Network Flow does it all ). So he will always choose the optimal strategy to win the game. And it’s a very very bad news for all the competitors.
But the girl did not want to see that happen so easily, because she’s not sure about her feelings towards him. So she wants to make Daizhenyang lose this game. She knows Daizhenyang will be the first to play the game. Your task is to help her determine whether her arrangement is a losable situation for Daizhenyang.
For simplicity, you are only told the position of head-up coins. And due to the girl’s complicated emotions, the same coin may be described twice or more times. The other coins are tail-up, of course.
Coins are numbered from left to right, beginning with 0.

Input

Multiple test cases, for each test case, the first line contains only one integer n (0<=n<=100), representing the number of head-up coins. The second line has n integers a1, a2 … an (0<=ak<10^8) indicating the An-th coin is head up.

Output

Output a line for each test case, if it’s a losable situation for Daizhenyang can, print “Yes”, otherwise output “No” instead.

Sample Input

0
1
0
4
0 1 2 3

Sample Output

Yes
No
Yes

  • 题意:每次翻转1,2,3个硬币,最右侧的翻转的一定是向上的。不能操作的输。

  • 分析:有结论,每个硬币sg值独立,然后推导sg值即可。具体见 kuangbin的翻硬币游戏.本题对应约束条件6.

  • 代码:

#include
using namespace std;
int a[105];
int sg(int n)
{
    int ans = 2 * n;
    int cnt = 0;
    while (n)
    {
        if (n & 1) cnt ++;
        n >>= 1;
    }
    if (cnt & 1) return ans;
    else return ans + 1;
}
int main(){
    //int T,x,n,m;
    //scanf("%d",&T);
    int n;
    while(scanf("%d",&n) != EOF){
        if (n == 0) { puts("Yes"); continue; }
        for (int i = 0; i < n; i++)
            scanf("%d", &a[i]);
        sort(a,a+n);
        int m = unique(a,a+n) - a;
        int ans = 0;
        for (int i = 0; i < m; i++)
            ans = ans ^ sg(a[i]);
        if (!ans) puts("Yes");
        else puts("No");
    }
    return 0;
}

HDU 3389 Game 构造 Nim博弈

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Problem Description

Bob and Alice are playing a new game. There are n boxes which have been numbered from 1 to n. Each box is either empty or contains several cards. Bob and Alice move the cards in turn. In each turn the corresponding player should choose a non-empty box A and choose another box B that B

Input

The first line contains an integer T (T<=100) indicating the number of test cases. The first line of each test case contains an integer n (1<=n<=10000). The second line has n integers which will not be bigger than 100. The i-th integer indicates the number of cards in the i-th box.

Output

For each test case, print the case number and the winner’s name in a single line. Follow the format of the sample output.

Sample Input

2
2
1 2
7
1 3 3 2 2 1 2

Sample Output

Case 1: Alice
Case 2: Bob

#include
using namespace std;
int main(){
    int T, cas = 0;
    scanf("%d",&T);
    //int n;
    while(T--){
        int n;
        scanf("%d",&n);
        int ans = 0;
        for (int i = 1; i <= n; i++)
        {
            int x;
            scanf("%d", &x);
            if ((i % 3 == 0 && i % 2 == 0) || i % 3 == 2)
                ans ^= x;
        }
        printf("Case %d: ",++cas);
        if (ans) puts("Alice");
        else puts("Bob");
    }
    return 0;
}

HDU 3032 Nim or not Nim? Nim博弈

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Problem Description

Nim is a two-player mathematic game of strategy in which players take turns removing objects from distinct heaps. On each turn, a player must remove at least one object, and may remove any number of objects provided they all come from the same heap.
Nim is usually played as a misere game, in which the player to take the last object loses. Nim can also be played as a normal play game, which means that the person who makes the last move (i.e., who takes the last object) wins. This is called normal play because most games follow this convention, even though Nim usually does not.
Alice and Bob is tired of playing Nim under the standard rule, so they make a difference by also allowing the player to separate one of the heaps into two smaller ones. That is, each turn the player may either remove any number of objects from a heap or separate a heap into two smaller ones, and the one who takes the last object wins.

Input

Input contains multiple test cases. The first line is an integer 1 ≤ T ≤ 100, the number of test cases. Each case begins with an integer N, indicating the number of the heaps, the next line contains N integers s[0], s[1], …, s[N-1], representing heaps with s[0], s[1], …, s[N-1] objects respectively.(1 ≤ N ≤ 10^6, 1 ≤ S[i] ≤ 2^31 - 1)

Output

For each test case, output a line which contains either “Alice” or “Bob”, which is the winner of this game. Alice will play first. You may asume they never make mistakes.

Sample Input

2
3
2 2 3
2
3 3

Sample Output

Alice
Bob

  • 题意:n堆石子的Nim博弈,增加一个操作将一堆石子分成2个小堆,不能操作的输。

  • 分析:由于n非常大,肯定需要打表的,发现规律,n = 4k时,sg为n - 1. n = 4k+3时,sg为n + 1。

  • 代码(附打表代码):

#include
using namespace std;
int mark[105],sg[105];
/*
void make_sg()
{
    sg[0] = 0;
    sg[1] = 1;
    for (int i = 2; i <= 100; i++)
    {
        memset(mark, 0, sizeof mark);
        for (int j = 0; j < i; j++)
            {
                mark[sg[j] ^ sg[i - j]] = 1;
                mark[sg[j]] = 1;
            }
        for (int j = 0;; j++)
            if (mark[j] == 0)
        {
            sg[i] = j;
            break;
        }
    }
}
*/
int get_sg(int x)
{
    if (x == 0) return 0;
    if (x % 4 == 0) return x - 1;
    if (x % 4 == 3) return x + 1;
    return x;
}
int main(){
    int T, cas = 0;
    scanf("%d",&T);
  /*
    make_sg();
    for (int i = 0; i < 100; i++)
        printf("%d %d\n",i,sg[i]);
    */
    while(T--){
        int n;
        scanf("%d",&n);
        int ans = 0;
        for (int i = 1; i <= n; i++)
        {
            int x;
            scanf("%d",&x);
            ans ^= get_sg(x);
        }
        if (ans) puts("Alice");
        else puts("Bob");
    }
    return 0;
}

HDU 2897 邂逅明下 博弈

Problem Description

当日遇到月,于是有了明。当我遇到了你,便成了侣。
那天,日月相会,我见到了你。而且,大地失去了光辉,你我是否成侣?这注定是个凄美的故事。(以上是废话)
小t和所有世俗的人们一样,期待那百年难遇的日食。驻足街头看天,看日月渐渐走近,小t的脖子那个酸呀(他坚持这个姿势已经有半个多小时啦)。他低下仰起的头,环顾四周。忽然发现身边竟站着位漂亮的mm。天渐渐暗下,这mm在这街头竟然如此耀眼,她是天使吗?站着小t身边的天使。
小t对mm惊呼:“缘分呐~~”。mm却毫不含糊:“是啊,500年一遇哦!”(此后省略5000字….)
小t赶紧向mm要联系方式,可mm说:“我和你玩个游戏吧,赢了,我就把我的手机号告诉你。”小t,心想天下哪有题目能难倒我呢,便满口答应下来。mm开始说游戏规则:“我有一堆硬币,一共7枚,从这个硬币堆里取硬币,一次最少取2枚,最多4枚,如果剩下少于2枚就要一次取完。我和你轮流取,直到堆里的硬币取完,最后一次取硬币的算输。我玩过这个游戏好多次了,就让让你,让你先取吧~”
小t掐指一算,不对呀,这是不可能的任务么。小t露出得意的笑:“还是mm优先啦,呵呵~”mm霎时愣住了,想是对小t的反应出乎意料吧。
她却也不生气:“好小子,挺聪明呢,要不这样吧,你把我的邮箱给我,我给你发个文本,每行有三个数字n,p,q,表示一堆硬币一共有n枚,从这个硬币堆里取硬币,一次最少取p枚,最多q枚,如果剩下少于p枚就要一次取完。两人轮流取,直到堆里的硬币取完,最后一次取硬币的算输。对于每一行的三个数字,给出先取的人是否有必胜策略,如果有回答WIN,否则回答LOST。你把对应的答案发给我,如果你能在今天晚上8点以前发给我正确答案,或许我们明天下午可以再见。”
小t二话没说,将自己的邮箱给了mm。当他兴冲冲得赶回家,上网看邮箱,哇!mm的邮件已经到了。他发现文本长达100000行,每行的三个数字都很大,但是都是不超过65536的整数。小t看表已经下午6点了,要想手工算出所有结果,看来是不可能了。你能帮帮他,让他再见到那个mm吗?

Input

不超过100000行,每行三个正整数n,p,q。

Output

对应每行输入,按前面介绍的游戏规则,判断先取者是否有必胜策略。输出WIN或者LOST。

Sample Input

7 2 4
6 2 4

Sample Output

LOST
WIN

  • 题意:n个物品,一次取[p,q]个,少于p个必须全拿,最后一个拿的人输。
  • 分析:直接打表就可以。也可以大致推导一下直接看到1—p必败,p+1—p+q必胜,循环。
  • 代码:
#include
using namespace std;
int sg[101],mark[101];
int main(){
    int n,p,q;
    while(scanf("%d%d%d",&n,&p,&q) != EOF){
        /*
        for (int i = 1; i <= p; i++)
        sg[i] = 0;
        for (int i = p + 1;i<=100;i++)
        {
            memset(mark, 0 ,sizeof mark);
            for (int j = p; j <= q; j++)
                if (i >j) mark[sg[i - j]] = 1;
            for (int j = 0;; j++)
            if (!mark[j]){
                sg[i] = j;
                break;
            }
        }
        for (int i = 1; i < 100; i++)
           printf("%d %d\n",i,sg[i]);
        */
        n %= (p + q);
        if (n == 0) n = p + q;
        if (n <= p) puts("LOST");
        else puts("WIN");
    }
    return 0;
}

HDU 2516 取石子游戏 FIB nim博弈

Problem Description

1堆石子有n个,两人轮流取.先取者第1次可以取任意多个,但不能全部取完.以后每次取的石子数不能超过上次取子数的2倍。取完者胜.先取者负输出"Second win".先取者胜输出"First win".

Input

输入有多组.每组第1行是2<=n<2^31. n=0退出.

Output

先取者负输出"Second win". 先取者胜输出"First win".
参看Sample Output.

Sample Input

2
13
10000
0

Sample Output

Second win
Second win
First win

  • 题意:显然。
  • 分析:本来想无脑上个sg函数看看,结果无从下手,因为每一次取的个数和上一次有关,sg函数只有局面,显然无法判定记住上一次的取的次数。所以只能自己暴力的试,毕竟是只有n 和 yes/no 这一个关系,然后找规律猜想。查题解时发现是个斐波那契博弈,学习新姿势。。链接
  • 代码:
#include
using namespace std;
int f[50];
int main(){
    f[0]=1;f[1]=2;
    for (int i=2;i<45;i++)
        f[i]=f[i-1]+f[i-2];
    int n;
    while(scanf("%d",&n)){
        if (n == 0) break;
        int i;
        for (i=0;i<45;i++)
        if (f[i]==n) break;
        if (i<45)
            puts("Second win");
        else puts("First win");
    }
    return 0;
}

HDU 2147 kiki’s game 博弈,简单规律

Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 40000/10000 K (Java/Others)

Problem Description

Recently kiki has nothing to do. While she is bored, an idea appears in his mind, she just playes the checkerboard game.The size of the chesserboard is n*m.First of all, a coin is placed in the top right corner(1,m). Each time one people can move the coin into the left, the underneath or the left-underneath blank space.The person who can’t make a move will lose the game. kiki plays it with ZZ.The game always starts with kiki. If both play perfectly, who will win the game?

Input

Input contains multiple test cases. Each line contains two integer n, m (0

Output

If kiki wins the game printf “Wonderful!”, else “What a pity!”.

Sample Input

5 3
5 4
6 6
0 0

Sample Output

What a pity!
Wonderful!
Wonderful!

  • 题意:(1,m)处放一个硬币,每次操作可以向左,向下,和左下移动一次,不能操作的输。
  • 分析:从左下角倒推每个坐标的必胜、必败态。
  • 代码:
#include
using namespace std;
int main(){
    int n,m;
    while(scanf("%d%d",&n,&m) && n && m){
        if ((n&1) && (m&1))
            puts("What a pity!");
        else puts("Wonderful!");
    }
    return 0;
}

HDU 1847 Good Luck in CET-4 Everybody! 简单博弈

Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Problem Description

大学英语四级考试就要来临了,你是不是在紧张的复习?也许紧张得连短学期的ACM都没工夫练习了,反正我知道的Kiki和Cici都是如此。当然,作为在考场浸润了十几载的当代大学生,Kiki和Cici更懂得考前的放松,所谓“张弛有道”就是这个意思。这不,Kiki和Cici在每天晚上休息之前都要玩一会儿扑克牌以放松神经。
“升级”?“双扣”?“红五”?还是“斗地主”?
当然都不是!那多俗啊~
作为计算机学院的学生,Kiki和Cici打牌的时候可没忘记专业,她们打牌的规则是这样的:
1、 总共n张牌;
2、 双方轮流抓牌;
3、 每人每次抓牌的个数只能是2的幂次(即:1,2,4,8,16…)
4、 抓完牌,胜负结果也出来了:最后抓完牌的人为胜者;
假设Kiki和Cici都是足够聪明(其实不用假设,哪有不聪明的学生~),并且每次都是Kiki先抓牌,请问谁能赢呢?
当然,打牌无论谁赢都问题不大,重要的是马上到来的CET-4能有好的状态。

Good luck in CET-4 everybody!

Input

输入数据包含多个测试用例,每个测试用例占一行,包含一个整数n(1<=n<=1000)。

Output

如果Kiki能赢的话,请输出“Kiki”,否则请输出“Cici”,每个实例的输出占一行。

Sample Input

1
3

Sample Output

Kiki
Cici

  • 题意:总数为n,每次抓牌抓的数量是2的幂次,不能操作的输。
  • 分析:懒得思考。。直接打表,发现3的倍数必败。
  • 代码:
#include
using namespace std;
int sg[105],mark[105];
void make_sg()
{
    sg[0] = 0;
    sg[1] = 1;
    sg[2] = 1;
    for (int i = 3; i <= 100; i++)
    {
        memset(mark, 0, sizeof mark);
        for (int j = 1; j <= i; j = j * 2)
            mark[sg[i - j]] = 1;
        for (int j = 0; ; j++)
        if (!mark[j]) {sg[i] = j; break;}
    }
    for (int i = 0; i <= 100; i++)
        printf("%d %d\n",i,sg[i]);
}
int main(){
    int n;
    while(scanf("%d",&n)!=EOF){
        //make_sg();
        if (n % 3 == 0)
            puts("Cici");
        else puts("Kiki");
    }
    return 0;
}

HDU 1846 Brave Game 简单巴什博弈

Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Problem Description

十年前读大学的时候,中国每年都要从国外引进一些电影大片,其中有一部电影就叫《勇敢者的游戏》(英文名称:Zathura),一直到现在,我依然对于电影中的部分电脑特技印象深刻。
今天,大家选择上机考试,就是一种勇敢(brave)的选择;这个短学期,我们讲的是博弈(game)专题;所以,大家现在玩的也是“勇敢者的游戏”,这也是我命名这个题目的原因。
当然,除了“勇敢”,我还希望看到“诚信”,无论考试成绩如何,希望看到的都是一个真实的结果,我也相信大家一定能做到的~

各位勇敢者要玩的第一个游戏是什么呢?很简单,它是这样定义的:
1、 本游戏是一个二人游戏;
2、 有一堆石子一共有n个;
3、 两人轮流进行;
4、 每走一步可以取走1…m个石子;
5、 最先取光石子的一方为胜;

如果游戏的双方使用的都是最优策略,请输出哪个人能赢。

Input

输入数据首先包含一个正整数C(C<=100),表示有C组测试数据。
每组测试数据占一行,包含两个整数n和m(1<=n,m<=1000),n和m的含义见题目描述。

Output

如果先走的人能赢,请输出“first”,否则请输出“second”,每个实例的输出占一行。

Sample Input

2
23 2
4 3

Sample Output

first
second

  • 题意:显然。
  • 分析:上面有一道也是巴什博弈,所以一眼看出来直接就是m+1倍数必败,此时无论先手取x为任意值,后手取m+1-x都必胜。
  • 代码:
#include
using namespace std;
int main(){
    int n,m;
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&m);
        if (n % (m + 1) != 0)
            puts("first");
        else puts("second");
    }
    return 0;
}

  • 题意:n*n棋盘,角落一个棋子,每次操作水平或数值移动,可以倒推sg函数,也可以直接斜对角线的画,也可以分析先手、后手,得到奇数先手必胜。
  • 代码:
#include
using namespace std;
int main(){
    int n;
    while(scanf("%d",&n) && n){
        if (n & 1)
            puts("ailyanlu");
        else puts("8600");
    }
    return 0;
}

HDU 1525 Euclid’s Game 博弈

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)

Problem Description

Two players, Stan and Ollie, play, starting with two natural numbers. Stan, the first player, subtracts any positive multiple of the lesser of the two numbers from the greater of the two numbers, provided that the resulting number must be nonnegative. Then Ollie, the second player, does the same with the two resulting numbers, then Stan, etc., alternately, until one player is able to subtract a multiple of the lesser number from the greater to reach 0, and thereby wins. For example, the players may start with (25,7):

25 7
11 7
4 7
4 3
1 3
1 0

an Stan wins.

Input

The input consists of a number of lines. Each line contains two positive integers giving the starting two numbers of the game. Stan always starts.

Output

For each line of input, output one line saying either Stan wins or Ollie wins assuming that both of them play perfectly. The last line of input contains two zeroes and should not be processed.

Sample Input

34 12
15 24
0 0

Sample Output

Stan wins
Ollie wins

  • 题意:用大的数减去小的数,能够使得其中的一个数变成0的人为胜者。
  • 分析:假设a>=b,如果ab 或者a%b0则先手为胜,当a>=2b时,则可以判断(a%b,b)是必胜点或者必败点,如果是必败点则可以走到这点,如果是必胜点则可以走到(a%b+b,b),当a < 2b,则可以通过 a-=b计算。
  • 证明:因为每次都是更相减损级的减少,所以迭代次数不多,每次P/N态转换,记录次数即可。
  • 代码:
#include
using namespace std;
int main(){
    int a,b;
    while(scanf("%d%d",&a,&b) && a && b){
        if (a < b) swap(a,b);
        int ok = 0;
        while (b)
        {
            if (a%b==0 || a/b >= 2) break;
            a -= b;
            swap(a,b);
            ok = 1 - ok;
        }
        if (ok) puts("Ollie wins");
        else puts("Stan wins");
    }
    return 0;
}

HDU 1850 Being a Good Boy in Spring Festival

Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Problem Description

一年在外 父母时刻牵挂
春节回家 你能做几天好孩子吗
寒假里尝试做做下面的事情吧

陪妈妈逛一次菜场
悄悄给爸爸买个小礼物
主动地 强烈地 要求洗一次碗
某一天早起 给爸妈用心地做回早餐

如果愿意 你还可以和爸妈说
咱们玩个小游戏吧 ACM课上学的呢~

下面是一个二人小游戏:桌子上有M堆扑克牌;每堆牌的数量分别为Ni(i=1…M);两人轮流进行;每走一步可以任意选择一堆并取走其中的任意张牌;桌子上的扑克全部取光,则游戏结束;最后一次取牌的人为胜者。
现在我们不想研究到底先手为胜还是为负,我只想问大家:
——“先手的人如果想赢,第一步有几种选择呢?”

Input

输入数据包含多个测试用例,每个测试用例占2行,首先一行包含一个整数M(1

Output

如果先手的人能赢,请输出他第一步可行的方案数,否则请输出0,每个实例的输出占一行。

Sample Input

3
5 7 9
0

Sample Output

1

  • 题意:m堆扑克牌,每次操作任选一堆,拿走任意数量,不能操作的输。
  • 分析:从m=3开始分析,(0,0,0)为初始必败态,很容易发现“奇异局势”,威佐夫博弈有这个。所有奇异局势都是必败态,算必胜策略个数也就是能构造出多少个不同的奇异局势,将结论推广到m堆,很容易得到算法:m堆的异或值,考虑去掉每堆的影响后的异或值,如果当前堆个数大于所需要成为奇异局势的异或值表示的个数,就可以先手拿走多的,也就是先手必胜的局势。
  • 代码:
#include
using namespace std;

int a[105];
int main(){
    int n;
    while(scanf("%d",&n) && n){
        int ans = 0;
        for (int i = 1; i <= n; i++)
            {
                scanf("%d",&a[i]);
                ans ^= a[i];
            }
        int sum = 0;
        for (int i = 1; i <= n; i++)
            if (a[i] > (ans^a[i]))
                sum ++;
        printf("%d\n",sum);
    }
    return 0;
}

HDU 2149 Public Sale 巴什博弈

Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Problem Description

虽然不想,但是现实总归是现实,Lele始终没有逃过退学的命运,因为他没有拿到奖学金。现在等待他的,就是像FarmJohn一样的农田生涯。

要种田得有田才行,Lele听说街上正在举行一场别开生面的拍卖会,拍卖的物品正好就是一块20亩的田地。于是,Lele带上他的全部积蓄,冲往拍卖会。

后来发现,整个拍卖会只有Lele和他的死对头Yueyue。

通过打听,Lele知道这场拍卖的规则是这样的:刚开始底价为0,两个人轮流开始加价,不过每次加价的幅度要在1~N之间,当价格大于或等于田地的成本价 M 时,主办方就把这块田地卖给这次叫价的人。

Lele和Yueyue虽然考试不行,但是对拍卖却十分精通,而且他们两个人都十分想得到这块田地。所以他们每次都是选对自己最有利的方式进行加价。

由于Lele字典序比Yueyue靠前,所以每次都是由Lele先开始加价,请问,第一次加价的时候,
Lele要出多少才能保证自己买得到这块地呢?

Input

本题目包含多组测试,请处理到文件结束(EOF)。每组测试占一行。
每组测试包含两个整数M和N(含义见题目描述,0

Output

对于每组数据,在一行里按递增的顺序输出Lele第一次可以加的价。两个数据之间用空格隔开。
如果Lele在第一次无论如何出价都无法买到这块土地,就输出"none"。

Sample Input

4 2
3 2
3 5

Sample Output

1
none
3 4 5

  • 题意:从0开始,每次加价1~n,大于等于m的胜利。求必胜策略。
  • 分析:巴什博弈模型,m整除(n+1)必输,必胜策略只能先m%(n+1),特判一下m<=n情况。
  • 代码:
#include
using namespace std;

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

HDU 1851 A Simple Game Nim套巴什博弈

Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/65535 K (Java/Others)

Problem Description

Agrael likes play a simple game with his friend Animal during the classes. In this Game there are n piles of stones numbered from 1 to n, the 1st pile has M1 stones, the 2nd pile has M2 stones, … and the n-th pile contain Mn stones. Agrael and Animal take turns to move and in each move each of the players can take at most L1 stones from the 1st pile or take at most L2 stones from the 2nd pile or … or take Ln stones from the n-th pile. The player who takes the last stone wins.

After Agrael and Animal have played the game for months, the teacher finally got angry and decided to punish them. But when he knows the rule of the game, he is so interested in this game that he asks Agrael to play the game with him and if Agrael wins, he won’t be punished, can Agrael win the game if the teacher and Agrael both take the best move in their turn?

The teacher always moves first(-_-), and in each turn a player must takes at least 1 stones and they can’t take stones from more than one piles.

Input

The first line contains the number of test cases. Each test cases begin with the number n (n ≤ 10), represent there are n piles. Then there are n lines follows, the i-th line contains two numbers Mi and Li (20 ≥ Mi > 0, 20 ≥ Li > 0).

Output

Your program output one line per case, if Agrael can win the game print “Yes”, else print “No”.

Sample Input

2
1
5 4
2
1 1
2 2

Sample Output

Yes
No

  • 题意:n堆石子,每堆有mi个,每堆每次最多取Li个,不能操作的输。
  • 分析:若只有一堆石子,就是巴什博弈,n堆石子取没有L的限制,就是Nim博弈,好题啊。。所以只需要将巴什博弈的先去掉,因为巴什博弈是确定的,剩下的Nim博弈就好了。
  • 代码:
#include
using namespace std;

int a[105];
int main(){
    int T,n;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        int ans = 0;
        for (int i = 0; i < n; i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            ans ^= (x % (y + 1));
        }
        if (!ans) puts("Yes");
        else puts("No");
    }
    return 0;
}

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

Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Problem Description

对于四川同胞遭受的灾难,全国人民纷纷伸出援助之手,几乎每个省市都派出了大量的救援人员,这其中包括抢险救灾的武警部队,治疗和防疫的医护人员,以及进行心理疏导的心理学专家。根据要求,我校也有一个奔赴灾区救灾的名额,由于广大师生报名踊跃,学校不得不进行选拔来决定最后的人选。经过多轮的考核,形势逐渐明朗,最后的名额将在“林队”和“徐队”之间产生。但是很巧合,2个人的简历几乎一模一样,这让主持选拔的8600很是为难。无奈,他决定通过捐款来决定两人谁能入选。
选拔规则如下:
1、最初的捐款箱是空的;
2、两人轮流捐款,每次捐款额必须为正整数,并且每人每次捐款最多不超过m元(1<=m<=10)。
3、最先使得总捐款额达到或者超过n元(0 我们知道,两人都很想入选志愿者名单,并且都是非常聪明的人,假设林队先捐,请你判断谁能入选最后的名单?

Input

输入数据首先包含一个正整数C,表示包含C组测试用例,然后是C行数据,每行包含两个正整数n,m,n和m的含义参见上面提到的规则。

Output

对于每组测试数据,如果林队能入选,请输出字符串"Grass", 如果徐队能入选,请输出字符串"Rabbit",每个实例的输出占一行。

Sample Input

2
8 10
11 10

Sample Output

Grass
Rabbit

  • 题意:裸的巴什博弈。。这就改一下输出。
  • 代码:
#include
using namespace std;

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

HDU 1849 Rabbit and Grass 博弈

Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Problem Description

大学时光是浪漫的,女生是浪漫的,圣诞更是浪漫的,但是Rabbit和Grass这两个大学女生在今年的圣诞节却表现得一点都不浪漫:不去逛商场,不去逛公园,不去和AC男约会,两个人竟然猫在寝食下棋……
说是下棋,其实只是一个简单的小游戏而已,游戏的规则是这样的:
1、 棋盘包含1*n个方格,方格从左到右分别编号为0,1,2,…,n-1;
2、 m个棋子放在棋盘的方格上,方格可以为空,也可以放多于一个的棋子;
3、 双方轮流走棋;
4、 每一步可以选择任意一个棋子向左移动到任意的位置(可以多个棋子位于同一个方格),当然,任何棋子不能超出棋盘边界;
5、 如果所有的棋子都位于最左边(即编号为0的位置),则游戏结束,并且规定最后走棋的一方为胜者。

对于本题,你不需要考虑n的大小(我们可以假设在初始状态,棋子总是位于棋盘的适当位置)。下面的示意图即为一个1*15的棋盘,共有6个棋子,其中,编号8的位置有两个棋子。

大家知道,虽然偶尔不够浪漫,但是Rabbit和Grass都是冰雪聪明的女生,如果每次都是Rabbit先走棋,请输出最后的结果。

Input

输入数据包含多组测试用例,每个测试用例占二行,首先一行包含一个整数m(0<=m<=1000),表示本测试用例的棋子数目,紧跟着的一行包含m个整数Ki(i=1…m; 0<=Ki<=1000),分别表示m个棋子初始的位置,m=0则结束输入。

Output

如果Rabbit能赢的话,请输出“Rabbit Win!”,否则请输出“Grass Win!”,每个实例的输出占一行。

Sample Input

2 3 5 3 3 5 6 0

Sample Output

Rabbit Win! Grass Win!

  • 题意:m堆石子,每次挑一堆减一点,不能操作的失败。
  • 分析:裸的nim,只不过包装成了棋子的背景。。
  • 代码:
#include
using namespace std;

int main(){
    int T,n,m;
    while(scanf("%d",&m) && m){
        int ans = 0;
        for (int i = 1; i <= m; i++)
        {
            int x;
            scanf("%d",&x);
            ans ^= x;
        }
        if (ans) puts("Rabbit Win!");
            else puts("Grass Win!");
    }
    return 0;
}

HDU 1517 A Multiplication Game博弈

Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)

Problem Description

Stan and Ollie play the game of multiplication by multiplying an integer p by one of the numbers 2 to 9. Stan always starts with p = 1, does his multiplication, then Ollie multiplies the number, then Stan and so on. Before a game starts, they draw an integer 1 < n < 4294967295 and the winner is who first reaches p >= n.

Input

Each line of input contains one integer number n.

Output

For each line of input output one line either Stan wins. or Ollie wins.
assuming that both of them play perfectly.

Sample Input

162
17
34012226

Sample Output

Stan wins.
Ollie wins.
Stan wins.

  • 题意:从1开始,每次乘2-9,大于等于n的赢。
  • 分析:巴什博弈的乘法情况,也很容易猜,用PN推也行。
  • 代码:
#include
using namespace std;
int main(){
    double n;
    while(scanf("%lf",&n)!=EOF){
        while (n > 18) n /= 18;
        if (n <= 9) puts("Stan wins.");
        else puts("Ollie wins.");
    }
    return 0;
}

HDU 2509 Be the Winner 反NIM博奕

Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Problem Description

Let’s consider m apples divided into n groups. Each group contains no more than 100 apples, arranged in a line. You can take any number of consecutive apples at one time.
For example “@@@” can be turned into “@@” or “@” or “@ @”(two piles). two people get apples one after another and the one who takes the last is
the loser. Fra wants to know in which situations he can win by playing strategies (that is, no matter what action the rival takes, fra will win).

Input

You will be given several cases. Each test case begins with a single number n (1 <= n <= 100), followed by a line with n numbers, the number of apples in each pile. There is a blank line between cases.

Output

If a winning strategies can be found, print a single line with “Yes”, otherwise print “No”.

Sample Input

2
2 2
1
3

Sample Output

No
Yes

  • 题意:反尼姆博弈
  • 分析:查反nim博弈。。
  • 代码:
#include
using namespace std;

int main(){
    int n;
    while (scanf("%d",&n)!=EOF)
    {
        int ok = 0, x, ans = 0;
        for (int i = 0; i < n; i++)
        {
            scanf("%d",&x);
            ans ^= x;
            if (x > 1) ok = 1;
        }
        if (!ok)
        {
            if (n%2==0) puts("Yes");
            else puts("No");
        }
        else {
            if (ans) puts("Yes");
            else puts("No");
        }
    }
    return 0;
}

HDU 1404 Digital Deletions

Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)

Problem Description

Digital deletions is a two-player game. The rule of the game is as following.

Begin by writing down a string of digits (numbers) that’s as long or as short as you like. The digits can be 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 and appear in any combinations that you like. You don’t have to use them all. Here is an example:

On a turn a player may either:
Change any one of the digits to a value less than the number that it is. (No negative numbers are allowed.) For example, you could change a 5 into a 4, 3, 2, 1, or 0.
Erase a zero and all the digits to the right of it.
The player who removes the last digit wins.
The game that begins with the string of numbers above could proceed like this:
Now, given a initial string, try to determine can the first player win if the two players play optimally both.

Input

The input consists of several test cases. For each case, there is a string in one line.

The length of string will be in the range of [1,6]. The string contains only digit characters.

Proceed to the end of file.

Output

Output Yes in a line if the first player can win the game, otherwise output No.

Sample Input

0
00
1
20

Sample Output

Yes
Yes
No
No

  • 题意: 给定一个数,两种操作,将任意一个数字减少,或者删掉0和右面的数,不能操作的数。
  • 分析:搜索打表sg函数。
  • 代码:
#include
using namespace std;

const int SZ = 1000000;
char a[10];
int sg[SZ];
int base[10];
    int n;
int get_length(int n)
{
    int sum = 0;
    while (n)
    {
        n /= 10;
        sum++;
    }
    return sum;
}
void extend(int n)
{
    int len = get_length(n);
    for (int i = len; i >= 1; i--)
    {
        //int base = 1;
        int x = n;
        //for (int j = 1; j < i; j++) base *= 10;
        int num = (n % base[i + 1]) / base[i];
        for (int j = num + 1; j <= 9; j++)
        {
             x += base[i];
             //
             //x = x - num * base + j * base;
             sg[x] = 1;
        }
    }
    if (len < 6)
    {
        int x = n;
        int t = 1;
        for (int i = len; i < 6; i++)
        {
            x *= 10;
            for (int b = 0; b < base[t]; b++)
                sg[x + b] = 1;
            t++;
        }
    }
}
void make_sg()
{
    base[1] = 1;
    for (int i = 2; i <= 7; i++)
        base[i] = base[i - 1] * 10;
    sg[0] = 1;
    for (int i = 1; i < SZ; i++)
        if (!sg[i]) extend(i);
}
int main(){
    make_sg();
    while(scanf("%s",a)!=EOF){
        if (a[0] == '0') {puts("Yes");continue;}
        int len = strlen(a);
        n = 0;
        for (int i = 0; i < len; i++)
            n = n * 10 + a[i] - '0';
        if (sg[n]) puts("Yes");
        else puts("No");
    }
    return 0;
}

HDU 1536 S-Nim 博弈

Time Limit: 5000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)

Problem Description

Arthur and his sister Caroll have been playing a game called Nim for some time now. Nim is played as follows:

The starting position has a number of heaps, all containing some, not necessarily equal, number of beads.

The players take turns chosing a heap and removing a positive number of beads from it.

The first player not able to make a move, loses.

Arthur and Caroll really enjoyed playing this simple game until they recently learned an easy way to always be able to find the best move:

Xor the number of beads in the heaps in the current position (i.e. if we have 2, 4 and 7 the xor-sum will be 1 as 2 xor 4 xor 7 = 1).

If the xor-sum is 0, too bad, you will lose.

Otherwise, move such that the xor-sum becomes 0. This is always possible.

It is quite easy to convince oneself that this works. Consider these facts:

The player that takes the last bead wins.

After the winning player’s last move the xor-sum will be 0.

The xor-sum will change after every move.

Which means that if you make sure that the xor-sum always is 0 when you have made your move, your opponent will never be able to win, and, thus, you will win.

Understandibly it is no fun to play a game when both players know how to play perfectly (ignorance is bliss). Fourtunately, Arthur and Caroll soon came up with a similar game, S-Nim, that seemed to solve this problem. Each player is now only allowed to remove a number of beads in some predefined set S, e.g. if we have S =(2, 5) each player is only allowed to remove 2 or 5 beads. Now it is not always possible to make the xor-sum 0 and, thus, the strategy above is useless. Or is it?

your job is to write a program that determines if a position of S-Nim is a losing or a winning position. A position is a winning position if there is at least one move to a losing position. A position is a losing position if there are no moves to a losing position. This means, as expected, that a position with no legal moves is a losing position.

Input

Input consists of a number of test cases. For each test case: The first line contains a number k (0 < k ≤ 100 describing the size of S, followed by k numbers si (0 < si ≤ 10000) describing S. The second line contains a number m (0 < m ≤ 100) describing the number of positions to evaluate. The next m lines each contain a number l (0 < l ≤ 100) describing the number of heaps and l numbers hi (0 ≤ hi ≤ 10000) describing the number of beads in the heaps. The last test case is followed by a 0 on a line of its own.

Output

For each position: If the described position is a winning position print a ‘W’.If the described position is a losing position print an ‘L’. Print a newline after each test case.

Sample Input

2 2 5
3
2 5 12
3 2 4 7
4 2 3 7 12
5 1 2 3 4 5
3
2 5 12
3 2 4 7
4 2 3 7 12
0

Sample Output

LWW
WWL

  • 题意:就是典型的Nim博弈,区别是先给出n种取法,每次只能取固定数字集合中的一个。
  • 分析:暴力打表。
  • 代码:
#include
using namespace std;
int sg[10010],s[110],vis[110];
int n,m;
void make_sg()
{
    sg[0] = 0;
    for (int i = 1; i <= 10000; i++)
    {
        memset(vis,0,sizeof vis);
        for (int j = 0; j < n; j++)
            if (i >= s[j])
                vis[sg[i - s[j]]] = 1;
            else break;
        for (int j = 0; ; j++)
        if (!vis[j]) {sg[i] = j; break;}
    }
}
int main(){

    while (scanf("%d",&n) && n)
    {
        for (int i = 0; i < n; i++)
            scanf("%d",&s[i]);
        memset(sg, -1 ,sizeof sg);
        sort(s, s + n);
        make_sg();
        scanf("%d",&m);
        while (m--)
        {
            int x;
            scanf("%d",&x);
            int ans = 0;
            while (x--)
            {
                int y;
                scanf("%d",&y);
                ans ^= sg[y];
            }
            if (!ans) putchar('L');
            else putchar('W');
        }
        puts("");
    }

    return 0;
}


HDU 1848 Fibonacci again and again

Time Limit: 1000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

Problem Description

任何一个大学生对菲波那契数列(Fibonacci numbers)应该都不会陌生,它是这样定义的:
F(1)=1;
F(2)=2;
F(n)=F(n-1)+F(n-2)(n>=3);
所以,1,2,3,5,8,13……就是菲波那契数列。
在HDOJ上有不少相关的题目,比如1005 Fibonacci again就是曾经的浙江省赛题。
今天,又一个关于Fibonacci的题目出现了,它是一个小游戏,定义如下:
1、 这是一个二人游戏;
2、 一共有3堆石子,数量分别是m, n, p个;
3、 两人轮流走;
4、 每走一步可以选择任意一堆石子,然后取走f个;
5、 f只能是菲波那契数列中的元素(即每次只能取1,2,3,5,8…等数量);
6、 最先取光所有石子的人为胜者;

假设双方都使用最优策略,请判断先手的人会赢还是后手的人会赢。

Input

输入数据包含多个测试用例,每个测试用例占一行,包含3个整数m,n,p(1<=m,n,p<=1000)。
m=n=p=0则表示输入结束。

Output

如果先手的人能赢,请输出“Fibo”,否则请输出“Nacci”,每个实例的输出占一行。

Sample Input

1 1 1
1 4 1
0 0 0

Sample Output

Fibo
Nacci

  • 题意:3堆石子,2人轮流拿,每次取得只能是斐波那契数列的值。
  • 分析:比上一题还简单,把上一题的s数组换成斐波那契就ok了,3个sg值异或起来。
  • 代码:
#include
using namespace std;
int sg[1001],f[20],vis[110];
int n,m;
void make_sg()
{
    memset(sg,0,sizeof sg);

    for (int i = 1; i <= 1000; i++)
    {
        memset(vis,0,sizeof vis);
        for (int j = 1; f[j] <= i; j++)
            vis[sg[i - f[j]]] = 1;
            //else break;
        for (int j = 0; ; j++)
        if (!vis[j]) {sg[i] = j; break;}
    }
}
int main(){
    f[0]=f[1]=1;
    for (int i = 2; i <= 16; i++)
        f[i] = f[i - 1] + f[i - 2];
    make_sg();
    int m,n,p;
    while (scanf("%d%d%d",&m,&n,&p) != EOF)
    {
        if (m + n + p == 0) break;
        if ((sg[m]^sg[n]^sg[p]) == 0)
            puts("Nacci");
        else puts("Fibo");
    }
    return 0;
}


博弈专题的训练就告一段落了,总体来讲收获还不错,打sg函数也清晰了很多,一些比较少见的博弈,反博弈也都有了些了解,感谢kuangbin博客整理.

你可能感兴趣的:(算法复习)