ZOJ 1039 Number Game(SG博弈+状压+记忆化搜索)

来源:互联网 发布:淘宝广告图片素材 编辑:程序博客网 时间:2024/06/05 08:13

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.


 【题解】

 一道很有意思的博弈题,本来第SG函数不是很懂,现在感觉一下子熟悉了好多。

 题意就是两个人轮流选数:

 刚开始给出m个待选的数;

 A选择一个数n,那么:

 ①:n的倍数不能选;

 ②:不能选的数字的倍数与n的和也不能选;

 由题意知最多19个数,所以我们可以用19位二进制位表示每种状态,1表示不可选,0表示可选。

 所以定义数组dp[1<<19],保存所有的状态;

 根据题目给出的可选数,把对应位置为0;

 最后遍历搜索可用数的选择方案,找到必赢状态;

所谓必赢状态就是下一步一定是必输状态(用二进制表示就是当前数选完后预判下一步,如果二进制全为1,说明没有可选的数,即下一步为必输状态,所以当前操作就是必赢状态)。

具体看代码注释。


【AC代码】

#include<iostream>#include<cstdio>#include<algorithm>#include<cstring>#include<math.h>using namespace std;const int N=1<<19;int m,n;int dp[N];int get_dp(int x){    if(dp[x] != -1) return dp[x];    int vis[20000];    memset(vis,0,sizeof vis);    for(int i=2;i<=20;++i)//遍历所有的数    {        int ii=i-1;        int step=x;        if(!(1 & (step>>(ii-1))))//如果当前数可选        {            step = (step | (1<<(ii-1))) ;//当前位置为1(表示标记为不可选)            for(int j=1;j<=19;++j)//当前数的倍数和不可选数的和标记为不可选            {                if((1 & (step >> (j-1))) && i+j<=19)                    step = (step | (1<<(i+j-1))) ;            }vis[get_dp(step)] = 1;        }    }    for(int i=0;;++i)        if(!vis[i])          return dp[x] = i;}int main(){    int t,cnt=1;    int value;    memset(dp,-1,sizeof(dp));    dp[524287]=0;    scanf("%d",&t);    while(t--)    {        value=524287;//1<<19 -1        scanf("%d",&m);        for(int i=0;i<m;++i)        {            scanf("%d",&n);            value -= pow(2,n-2);//把能选的位置都置为0        }        printf("Scenario #%d:\n",cnt++);        if(!get_dp(value))            printf("There is no winning move.\n");        else        {            printf("The winning moves are:");            for(int i=2;i<=20;++i)            {                int ii=i-1;                int step=value;                step=(step | (1 << (ii-1)) );                for(int j=1;j<=19;++j)                {                    if((1 & (step>>(j-1))) && j+i<=19)                        step=(step | (1<<(j+i-1)));                }                if(get_dp(step) == 0)                    printf(" %d",i);            }            printf(".\n");        }        printf("\n");    }    return 0;}



原创粉丝点击