ZOJ 1039 Number Game (状态压缩,记忆化搜索)

来源:互联网 发布:kmeans聚类算法java 编辑:程序博客网 时间:2024/05/17 20:12

转载请注明出处,谢谢 http://blog.csdn.net/ACM_cxlove?viewmode=contents           by---cxlove

题目:2-20这19个数字的游戏。每取走一个数之后,这个数的倍数便不能再取,而且某两个不能取的数的和,也不能再取。

http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=39

由于只有19个数,通过状态压缩保存状态,进行记忆化搜索。dp[1<<19]即可,dp[i]表示可行集合为i时的必胜必败情况

接下来重要的是状态转移。

对于原状态state,如果要把数字x从集合中去除的话。首先要分为两个步骤

1、将x以及x的倍数去掉,这便是把一个数的特定位置0的操作,位运算轻松解决,详见代码

2、去除不在集合中的元素与x的倍数的和。这步可以倒过来实现,找到还在集合中的元素,从中减掉若干个x,判断这个新的数是否在集合中,如果不在,说明这个数是非法的,去除。也可以通过位运算解决

在必胜与必败中,N态必然有一个后继是P态,利用这点搞定。

最后要输出可行解,便是枚举删除某个数,判断是否转变为P态即可

#include<iostream>#include<cstdio>#include<ctime>#include<cstring>#include<cmath>#include<algorithm>#include<cstdlib>#include<vector>#define C    240#define TIME 10#define inf 1<<25#define LL long longusing namespace std;int dp[1<<19];int get_state(int state,int x){    int ret=state;    //把x以及x的倍数去除    //首先把要删除的那位置为1,然后取反,最后与状态相与    for(int i=x;i<=20;i+=x)        ret&=~(1<<(i-2));    for(int i=2;i<=20;i++){        //如果某个数还在集合当中,则判断是否有某个不在集合中的数和X的倍数组成        if((1<<(i-2))&ret){            for(int j=x;i-j-2>=0;j+=x)                //如果数i为某个不在集合中的数以及x的和,则去除                if(!((1<<(i-j-2))&ret)){                     ret&=~(1<<(i-2));                     break;                }        }    }    return ret;}int get_dp(int state){    if(dp[state]!=-1)        return dp[state];    for(int i=2;i<=20;i++){        if(state&(1<<(i-2))){            //将第i个数移出集合            int tmp=get_state(state,i);            //P态得到N态            if(!get_dp(tmp))                return dp[state]=1;        }    }    return dp[state]=0;}int main(){    memset(dp,-1,sizeof(dp));    dp[0]=0;    int t,n,a[20],cas=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",++cas);        if(get_dp(state)==0)            puts("There is no winning move.\n");        else{            printf("The winning moves are:");            for(int i=0;i<n;i++){                //枚举去除,是否为必败态                int tmp=get_state(state,a[i]);                if(!get_dp(tmp))                    printf(" %d",a[i]);            }            puts(".\n");        }    }    return 0;}



原创粉丝点击