poj 1143 Number Game 博弈 记忆化状态压缩搜索

5 篇文章 0 订阅

poj 1143 Number Game

http://poj.org/problem?id=1143

第一道博弈题,纠结了好久好久好久……

题意:给定2到20的数,当选择一个数后,次数的倍数不可再选,已选的数的和不可再选,例如选择了2和3,则2*2, 2*3, 2*4,(2+3) , (2*2+ 3*5)...这些数都不能再选

问最先走那步可必胜

 

对于一个状态(已走好),若能走到一个必胜的状态,则下一个人一定按必胜策略走,所以此状态必败

若一个状态无论怎样都走不到一个必胜的状态,则次状态必胜

状态0x1fffff,即所有数字全选情况下,为必胜

 

分享一组数据19 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

答案是 5 7 12 16 18 19

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
int vis[2100000], n, value, num[25]; //vis用来记录各个状态,1为必胜,-1必败,0未搜索过
int p2[25];
int add(int v, int nn){ //当加入某个数后,有哪些数相当于同时加了进去
    int i, j, aa= nn;
    v|= (p2[aa]);
    for( i= 2; i<= 20; i++){
        if( ((v & (p2[i]))) && ( aa+ i <= 20)){
            v|= p2[i+aa];
        }
    }
    return v;
}
bool dfs( int value ){
    if( vis[value]){
        if( vis[value]== -1) return false;
        return true;
    }
    int i, v;
    for( i= 2; i<= 20; i++){
        if( (value&p2[i])== 0){
            v= add(value, i);
            if( dfs(v)){
                vis[value]= -1;
                return false;
            }
        }
    }
    vis[value]= 1;
    return true;
}
int main(){
   // freopen("1.txt", "r", stdin);
    int i, j, k, v, a, b, text= 1, flag;
    memset( vis, 0, sizeof( vis));
    for( i=0; i<22; i++)
        p2[i]= (1<<i);
    while( scanf("%d", &n ) && n){
        printf("Test Case #%d\n", text++);
        value= 0x1fffff; //0~20共21位,1为已选,0为未选, 0x1fffff为全选状态
        for( i=0; i<n; i++){
            scanf("%d", &a);
            value-= p2[a]; // value 为初始状态
            num[i]= a;
        }
        //sort(num, num +n);
        //n=unique(num, num+n)-num; //有序数组去重,若从1开始,则n=unique(num+1, num+1+n)-(num+1)括号不可化简掉
        flag= 0;
        vis[0x1fffff]= 1;
        for( i=0; i<n; i++){
            v= add(value, num[i]);
            if(dfs( v)) {
                if( flag== 0){
                    printf("The winning moves are:");
                    flag= 1;
                }
                printf(" %d", num[i]);
            }
        }
        if( flag== 0) printf("There's no winning move.");
        printf("\n\n");
    }
    return 0;
}


 


 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值