poj1143(Number Game)

6 篇文章 0 订阅
5 篇文章 0 订阅
 

题目大意:

       Christine和Matt玩一个游戏.游戏的规则如下:一开始有一列数字(2~20),有的被列出,有的没有列出.从Christine开始,两人轮流划去一个已被列出的数字.每划去一个数字,其他的所有的能被不在这个数列的数字的两两整线性表示的数也自动从数列中退出.到最后,轮到某人而此时数列中没有元素时,这个人就输了.

       规定:winning moveà能将对手置于losing position的决策,而losing positionà不存在任何winning move的序列.

这两个概念都是在两人都play perfectly 的前提下提出的.

一些限制:

        time limit:1000MS      memory limit:10000k

输入:

        几组数据,每组数据占一行,每行开头为n初始序列中元素个数 (1≤n ≤20),其后跟n个数,表示序列中的元素.

         输入”0”表示结束.

输出:

        对于每组数据,输出所有的winning move.

Sample input:

       2   2   5

       2   2   3

       5   2   3   4   5   6

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

几个首先想到的明显的结论:

      (1) 数列中必定有2和3其中的至少一个,也即2和3可以整线性表示出后面所有的数.由此可推出:如果初始的数列只有2(3),那么此时的winning move只有一个:2(3).

     

       (2) 如果2和3都同时出现在序列中,在所有其他元素被取完之前, Christine和Matt是不敢碰2或者3的,因为一旦有个人取了其中的一个数,问题就回到了(1),这个人必败.此种情况下任何一个先取2(3)的人都是被逼迫的.所以这种情况可只考虑3之后的数,但这个结论对此题的优化似乎没什么明显的帮助. 

记忆化搜索.    

ResultàAccepted         Time:0MS   Memory:1260K

       可以用一个19位的二进制数来表示当前数列的状态,如:序列 2  3  4 可以表示成 111      序列 3  5  7  8 可以表示成 0101011  这样,可以开一个2^19(=52488)大小的数组用1或0来记录当前的状态:存在winning move或losing position.每个状态的下标即为此二进制数(从后往前计算)的值.

       这样,时间会省很多,只需在前一个代码的基础上加入记录的过程就行了.

       然后在计算二进制的值时,适当运用位操作,会避免累加2的N次方.代码如下:

#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>

using namespace std;

int n, t, zhuangtai[524387],result[25];

int getid(bool a[]){
    int flag = 0;
    for(int i = 2; i <= 20; i++,flag<<=1){
        if(a[i]) {
            flag |= 1;//如果哪项不为0,就要使得这一项变为1,如100然后第四项为0
        }
    }
                            //则要先使得1000变为1001,然后再进行以后的进制移动///
    //printf("ssd:%d\n",flag>>1);
    return flag >> 1;//for循环完成后多移动了一位
}
int dfs(bool arr[], int start){
    bool acpy[25];
    memcpy(acpy,arr,24);
    acpy[start] = 0;
    for(int i = 2; i + start <= 20; i ++){
        if(!acpy[i])
            acpy[i + start] = 0;
    }
    int id1 = getid(acpy);
    if(zhuangtai[id1] != 0){
        if(zhuangtai[id1] > 0)  return 1;//表示可以取得胜利
        else return 0;
    }
    for(int i = 2; i <= 20; i++){
        if(acpy[i] && !dfs(acpy,i)){
            zhuangtai[id1] = 1;
            return 1;
        }
    }
    zhuangtai[id1] = -1;
    return 0;
}

int main(){
    t = 0;
    bool arr[25];
    int id,a;
    while(scanf("%d", &n),n){
        memset(arr, 0, sizeof(arr));
        for(int i = 0; i < n; i++){
            scanf("%d", &a);
            arr[a] = 1;
        }
        id = getid(arr);
        int sum = 0;
        for(int i = 2; i <= 20; i++){
            if(arr[i] && !dfs(arr,i)){
                result[sum++] = i;
            }
        }
        t++;
        printf("Test Case #%d\n",t);
        if(sum == 0){
            zhuangtai[id] = -1;
            printf("There's no winning move.\n\n");
        }
        else{
            printf("The winning moves are:");
            for(int i = 0; i < sum; i++){
                printf(" %d",result[i]);
            }
            printf("\n\n");
        }
        //printf("id:%d\n", id);
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值