ZOJ-1039 Number Game(SG博弈)

来源:互联网 发布:网页前端开发知乎 编辑:程序博客网 时间:2024/05/20 11:32

Number Game

Background

Christiane and Matthias are playing a new game, the Number Game. The rules of the Number Game are:

Christian and Matthias take turns in choosing integer numbers greater than or equal to 2. The following rules restrict the set of numbers which may be chosen:

R1 A number which has already been chosen by one of the players or a multiple of such a number cannot be chosen. (A number z is a multiple of a number y if z can be written as y * x and x is a positive integer.)

R2 A sum of two such multiples cannot be chosen either.

R3 For simplicity, a number which is greater than 20 cannot be chosen either. This enables a lot more NPCs (Non-Personal-Computers) to play this game.

The player who cannot choose any number anymore looses the Number Game.

Here is an example: Matthias starts by choosing 4. Then Christiane is not allowed to choose 4, 8, 12, etc. Let us assume her move is 3. Now, the numbers 3, 6, 9, etc. are excluded, too; furthermore, numbers like: 7 = 3 + 4, 10 = 2 * 3 + 4, 11 = 3 + 2 * 4, 13 = 3 * 3 + 4, … are not available. So, in fact, the only numbers left are 2 and 5. Matthias now says 2. Since 5 = 2 + 3 is now forbidden, too, he wins because there is no number for Christiane’s move left.

Your task is to write a program which will help to play the Number Game. In general, i.e., without rule R3, this game may go on forever. However, with rule R3, it is possible to write a program that finds a strategy to win the game.

Problem

Given a game situation (a list of numbers which are not yet forbidden), your program should output all winning moves. A winning move is a move by which the player whose turn it is can force a win no matter what the other player will do. Now we define these terms more formally:

A loosing position is a position in which either

  1. all numbers are forbidden, or

  2. no winning move exists.

A winning position is a position in which a winning move exists.

A winning move is a move after which the position is a loosing position.

Input

The first line contains the number of scenarios.

The input for each scenario describes a game position. It begins with a line containing the number a, 0 <= a < 20 of numbers which are still available. Next follows a single line with the a numbers still available, separated by single blanks.

You may assume that all game positions in the input could really occur in the Number Game (for example, if 3 is not in the list of numbers available, 6 will not be, either).

Output

The output for each scenario begins with a line containing “Scenario #i:” where i is the number of the scenario starting at 1. In the next line either print “There is no winning move.” if this is true for the position of the current scenario, or “The winning moves are: w1 w2 … wk.” where the wi are all the winning moves, in ascending order, separated by single blanks. The output for each scenario should be followed by a blank line.

Sample Input

2
1
2
2
2 3

Sample Output

Scenario #1:
The winning moves are: 2.

Scenario #2:
There is no winning move.

题意:两个人轮流选数,选的数x有以下要求:
①,2<=x<=20
②,已选数的倍数不能选
③,不同已选数的倍数的和不能选
当没有可以选的数的时候,当前选数的人就算输。
比如说,如果第一个人选了2,那么根据要求②2,4,6,8,10,12,14,16,18,20都不能选了,如果第二个人选了3,那么根据要求②3,6,9,12,15,18都不能选了,再根据要求③可知,2+3=5,5不能选,2*2+3=7,7不能选,2*4+3=11,11不能选,2*2+3*3=13,13不能选,2+3*5=17,17不能选,2*2+3*5=19,19不能选,所以所有的数都选不了了,那么第一个人就输了。
现在给你a个数,表示还未选的数,(不可能出现有6没有3,有8没有2这样的情况),然后问你第一步选哪些数一定可以获胜。

思路:这是一道很明显的博弈题,因为这道题只有19个数(2到20),所以我们可以用二进制来储存当前的状态,假设一开始所有的数都不能选,那么state的初始值就应该赋为2^19-1,然后根据题目所给的可以选的数把对应的二进制位改为0,那么最初的state值就知道了。然后就是找SG值,我们知道state=2^19-1的时候就没有可以选的的数了,所以SG[2^19-1]=0。然后当state!=2^19-1的时候,就肯定还有数可以选,我们从2到20循环一遍,能选的数就是当前状态的后续状态,值得注意的一点是,如果我当前选了i这个数,那么我要从2到20循环一遍,把所有不能选的数加上i后的数也标记为不能选,这样才不会有遗漏。输出结果的时候也要把2到20枚举一遍,如果后续状态的SG值为0,那么才说明选当前这个数是必胜的。

#include <iostream>#include <cstdio>#include <cstring>#include <string>#include <cstdlib>#include <cmath>#include <vector>#include <queue>#include <map>#include <algorithm>#include <set>#include <functional>using namespace std;typedef long long LL;typedef unsigned long long ULL;const int INF = 1e9 + 5;const int MAXN = 1000005;const int MOD = 1000000007;const double eps = 1e-8;const double PI = acos(-1.0);int SG[524288];//2^19int getSG(int x){    if (SG[x] != -1)//记忆化搜索        return SG[x];    int vis[10005];    memset(vis, 0, sizeof vis);    for (int m = 2; m <= 20; m++)//枚举要选的数    {        int i = m - 1;//该数的位置        int STA = x;        if (!(1 & (STA >> (i - 1))))//如果该数可以选        {            STA = (STA | (1 << (i - 1)));//将该数标记为不可选            for (int j = 1; j <= 19; j++)//把不满足要求②③的数标记为不可选                if ((1 & (STA >> (j - 1))) && j + m <= 19)                    STA = (STA | (1 << (j + m - 1)));            vis[getSG(STA)] = 1;        }    }    for (int i = 0;; i++)        if (!vis[i])            return SG[x] = i;}int main(){    int T;    int a;    int state;//状态    int STA;//临时状态    int x;    memset(SG, -1, sizeof SG);    SG[524287] = 0;//终止状态    scanf("%d", &T);    for (int CAS = 1; CAS <= T; CAS++)    {        state = 524287;//假设初始全部不能选        scanf("%d", &a);        for (int i = 1; i <= a; i++)        {            scanf("%d", &x);            state -= pow(2, x - 2);//把能选的位置变成0        }        printf("Scenario #%d:\n", CAS);        if (getSG(state) == 0)//必败态            printf("There is no winning move.\n");        else        {            printf("The winning moves are:");            for (int m = 2; m <= 20; m++)//枚举第一步要选的数            {                int i = m - 1;//该数的位置                STA = state;//把当前状态赋给临时状态                STA = (STA | (1 << (i - 1)));                for (int j = 1; j <= 19; j++)                    if ((1 & (STA >> (j - 1))) && j + m <= 19)                        STA = (STA | (1 << (j + m - 1)));                if (getSG(STA) == 0)//如果后续状态为必败态说明当前这一步必胜                    printf(" %d", m);            }            printf(".\n");        }        printf("\n");    }}
原创粉丝点击