zoj1039 Number Game 博弈+DP+状态压缩

来源:互联网 发布:中药粉碎机淘宝 编辑:程序博客网 时间:2024/05/22 04:48
Number Game

Time Limit: 10 Seconds      Memory Limit: 32768 KB

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.


Source: Mid-Central European Regional Contest 2000

 

学了点状态压缩的知识。

要从一个状态里去掉某个位置的数  state&=~(1<<(i))

要给一个状态加入某个位置的数state|=(1<<i)

判断一个状态里是否包含某个位置的数 if(state&(1<<i))  为1则包含

有错误请指出~

题意:就是从2~20这19个数里选择数对弈,每次取走一个数后,这个数的倍数的数不能再取,而且某两个不能取的数的和也不能再取。

因为只有19个数,所以我们可以进行状态压缩,总的状态为1<<19。

取走一个数后怎么去除这个数以及这个数的倍数呢?

for(int i=x;i<=20;i+=x)

state&=~(1<<i);

怎么把不在集合里的数与这个数的和去掉呢?

这里我们需要逆向思考,假设某个数在这个集合里,那么用它不断减去x, 如果得到的差值不在这个集合里,那么这个数是非法的,所以要去掉。

判断一个状态的输赢用的就是博弈的PN的基本性质了,不再赘述了。

#include<iostream>#include<cstdlib>#include<stdio.h>#include<memory.h>using namespace std;int dp[524289];int get_solve(int state,int x){    //将x和x的倍数去除    int ret=state;    for(int i=x;i<=20;i+=x)    ret&=~(1<<(i-2));    //将不在集合里的与x的倍数的和    for(int i=2;i<=20;i++)    {        if(ret&(1<<(i-2)))//如果数i在集合里        for(int j=x;i-j-2>=0;j+=x)        {            if(!(ret&(1<<(i-j-2))))            {                ret&=~(1<<(i-2));                break;//            }        }    }    return ret;}int dfs(int state){    if(dp[state]!=-1) return dp[state];    for(int i=2;i<=20;i++)    {        if(state&(1<<(i-2)))        {            if(dfs(get_solve(state,i))==0)            return dp[state]=1;        }    }    return dp[state]=0;}int main(){    //cout<<(1<<19)<<endl;    int t,n,a[21];    int count=1;    memset(dp,-1,sizeof(dp));    dp[0]=0;    scanf("%d",&t);    while(t--)    {        scanf("%d",&n);        int state=0;        for(int i=0;i<n;i++)        {            scanf("%d",&a[i]);            state|=1<<(a[i]-2);        }        printf("Scenario #%d:\n",count++);        if(dfs(state)==0)        puts("There is no winning move.\n");        else        {            printf("The winning moves are:");            for(int i=0;i<n;i++)            {                    if(dfs(get_solve(state,a[i]))==0)                        printf(" %d",a[i]);            }            puts(".\n");        }    }}