POJ 1143Number Game

Description
Christine and Matt are playing an exciting game they just invented: the Number Game. The rules of this game are as follows.
The players take turns choosing integers greater than 1. First, Christine chooses a number, then Matt chooses a number, then Christine again, and so on. The following rules restrict how new numbers may be chosen by the two players:

A number which has already been selected by Christine or Matt, or a multiple of such a number,cannot be chosen.
A sum of such multiples cannot be chosen, either.

If a player cannot choose any new number according to these rules, then that player loses the game.
Here is an example: Christine starts by choosing 4. This prevents Matt from choosing 4, 8, 12, etc.Let’s assume that his 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 also not available. So, in fact, the only numbers left are 2 and 5. Christine now selects 2. Since 5=2+3 is now forbidden, she wins because there is no number left for Matt to choose.
Your task is to write a program which will help play (and win!) the Number Game. Of course, there might be an infinite number of choices for a player, so it may not be easy to find the best move among these possibilities. But after playing for some time, the number of remaining choices becomes finite, and that is the point where your program can help. Given a game position (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 who is about to move can force a win, no matter what the other player will do afterwards. More formally, a winning move can be defined as follows.

A winning move is a move after which the game position is a losing position.
A winning position is a position in which a winning move exists. A losing position is a position in which no winning move exists.
In particular, the position in which all numbers are forbidden is a losing position. (This makes sense since the player who would have to move in that case loses the game.)

Input
The input consists of several test cases. Each test case is given by exactly one line describing one position.
Each line will start with a number n (1 <= n <= 20), the number of integers which are still available. The remainder of this line contains the list of these numbers a1;…;an(2 <= ai <= 20).
The positions described in this way will always be positions which can really occur in the actual Number Game. For example, if 3 is not in the list of allowed numbers, 6 is not in the list, either.
At the end of the input, there will be a line containing only a zero (instead of n); this line should not be processed.

Output
For each test case, your program should output “Test case #m”, where m is the number of the test case (starting with 1). Follow this by either “There’s no winning move.” if this is true for the position described in the input file, or “The winning moves are: w1 w2 … wk” where the wi are all winning moves in this position, satisfying wi < wi+1 for 1 <= i < k. After this line, output a blank line.

Sample Input

2 2 5
2 2 3
5 2 3 4 5 6
0

Sample Output

Test Case #1
The winning moves are: 2

Test Case #2
There’s no winning move.

Test Case #3
The winning moves are: 4 5 6
原题链接
题目大意是:两个人从2到20中选择数字,每选择一个数字,这个数字的倍数,以及这个数字得倍数和原有数字的和都被去掉(不能再选择),最后没有数字可选的人输掉比赛
这道题的思想跟 Can I Win 是一样的,我们之前分析过,链接为打开链接, 不同之处在于状态的更新更复杂。
状态更新为:每加入一个数,这个数的倍数都要去掉,这个数的倍数与原有数的和都要被去掉。 一开始想的很复杂,后来参考了大神的代码,只用一个循环就解决了。

for(int i = 2; i + st <= 20; i++) { //更新状态
        if(0 == v[i]) {
            v[i+st] = 0;
        }
    }

比如先选择了5,则 5,10,15,20被标记。接着选择2,先将2 标记, 然后从2 到 20 遍历,当i=2时,相当于2*2,4被标记,当i=4时,相当于2*3,6被标记,这样 2 的倍数都可以被标记, 当i=5时,7被标记,相当于2 + 5,当i=7时,9被标记,相当于2*2+5,这样,2的倍数和原有数字的和被标记。

代码中既可以使用数组标记,也可以使用map标记,map更节省空间。
具体代码如下:

#include <iostream>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <stdlib.h>
#include <map>
using namespace std;

map<int,bool> mp;
int a[25];

int getNum(int a[]) {
    int res = 0;
    for(int i = 2; i <= 20; i++) {
        if(a[i]) res |= 1;
        res <<= 1;
    }
    return res;
}

int dfs(int s[], int st) {
    int v[25];
    memcpy(v,s,25*sizeof(int));
    v[st] = 0;
    for(int i = 2; i + st <= 20; i++) { //更新状态
        if(0 == v[i]) {
            v[i+st] = 0;
        }
    }
    int ss= getNum(v);
    if(mp.count(ss)) {
        return mp[ss];
    }
    for(int i = 2; i <= 20; i++) {
        if(v[i] &&  !dfs(v,i)) {
            mp[ss] = true;
            return true;
        }
    }
    mp[ss] = false;
    return false;
}

int main() {
    //freopen("input.txt","r",stdin);
    int ans[25];
    int b;
    int n;
    int cas = 0;
    while(cin>>n) {
        cas++;
        memset(a,0,sizeof(a));
        if(n == 0) {
            break;
        }
        for(int i = 0; i < n; i++) {
            cin>>b;
            a[b] = 1;
        }
        int tot = 0;
        for(int i = 2; i <= 20; i++) {
            if(a[i] && !dfs(a,i)) ans[tot++] = i;
        }
        cout<<"Test Case #"<<cas<<endl;
        if(tot == 0) cout<<"There's no winning move."<<endl;
        else {
            cout<<"The winning moves are:"<<" ";
            for(int i = 0; i < tot; i++) {
                if(i != tot- 1) cout<<ans[i]<<" ";
                else cout<<ans[i]<<endl;
            }
        }
        cout<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhengjihao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值