hdu4317 Unfair Nim 二进制状态压缩

本文深入探讨了Nim游戏的一种特殊策略,通过分析二进制表示下的棋子分布,利用动态规划方法解决如何通过最少的操作确保玩家获胜的问题。

题意:

两个人玩Nim游戏,Alice和Bob 。Alice先一步。 Alice允许Bob 在任意一堆石子上 加任意的石头,使得Bob赢。问Bob 最少加多少石子。如果Bob不可能让自己赢,就输出impossible。

乍一看貌似是博弈论,仔细分析一下其实是障眼法。。。 

将n堆棋子上的数都看成是2进制。Nim游戏的规则是:每个人只允许在其中一堆中拿任意的石子。 假设n个二进制数 的每一位都有偶数个1(比如个位:将第一个数的个位和第二个数的个位...第n个数的个位看成一列,即其中1的个数为偶数个)  ,也就是说每一位都异或为0。那么第一个人取得任意的石子,都必将会使其中某些位不再异或为0(正由于只允许在一堆上取石子造成的),那么第二个人总可以再另外一堆石子上取对应的石子使得每一位再次恢复异或为0。 按照这样下去,石子数量逐渐减少,所以最后的石子肯定是由第二个人取得,因为第一个人取之后所有位必定不能全异或为0。

到这里题目的真正目的就出来了:对于n个二进制数中的某一个数,对它加上一个数后,能否使得每一位都异或为0。如果能,输出加上的数中最小的那一个。

石子堆数为个位数的数量级,所以用状态压缩是不会te的。

将n个数的第k位 排成一列,作为该题的状态。从低位到高位进行dp。

dp[ i ][ j ]: 第i 位,上一位进位状态为 j 时 满足前 i 位 异或为0,所需添加的最小石子数。

s[ i ] 初始第 i 位中的二进制数

a[ ] 存初始每一堆的数

t[m ] 预处理 某一位状态为m 时,1的个数。

解释几个位运算代表的意思:

 j & s[r] 进位和初始状态相加后产生的进位

s[ r ] ^ j 相加进位之后剩下的数

k ^ tmp  某一位需要的进位

判断状态是否可以转移时有3个条件:

1.规定了k的每一位进位必须大于或等于tmp的每一位 :(k&tmp)==tmp

2.规定了如果要继续进位成k则要求s[i]^j的那一位至少为1 :((s[r]^j)&(k^tmp))==(k^tmp)

3.规定该进位下的1的个数为偶数,或者为奇数但前提是个数少于n,因为可以任意补一个1使得为偶数,不影响进位 :((t[(s[r]^j)^(k^tmp)]&1)==0||t[(s[r]^j)^(k^tmp)]<N)


状态转移方程:

dp[r][k]=min(dp[r][k],dp[r-1][j]+(t[k^tmp]+(t[(s[r]^j)^(k^tmp)]&1))*(1<<(r-1)))


需要注意一下位运算的优先级,以免括号打错。

具体上代码了。。。


#include<iostream>
#include<cstdio>
#include<string.h>
#include<algorithm>
using namespace std;
const int M=1<<11;
const int MAX=0x1f1f1f1f;
int s[25],dp[25][M];//dp[i][j]:第i列,上一列进位状态为j时的最小加石子数
int t[M];//状态为M的二进制数中1 的个数
int a[11];//初始石子数

int main()
{
    int N,i,j,k,len,tmp,r;
    for(i=0;i<M;i++)
    {
        tmp=0;
        j=i;
        while(j!=0)
        {
            tmp+=(j&1);
            j=j>>1;
        }
        t[i]=tmp;
    }
    while(scanf("%d",&N)!=EOF)
    {
        memset(s,0,sizeof(s));;
        for(i=0;i<N;i++)
        {
            scanf("%d",&a[i]);
        }
        if(N<2)
        {
            printf("impossible\n");
            continue;
        }
        for(i=1;i<=22;i++)
        {
            for(j=0;j<N;j++)
            {
                if(a[j]&(1<<(i-1)))
                {
                    s[i]|=(1<<j);//二进制加
                }
            }
            if(s[i]!=0)
            len=i+1;
        }
        memset(dp,MAX,sizeof(dp));
        dp[0][0]=0;
        for(r=1;r<=len;r++)
        {
            for(j=0;j<(1<<N);j++)
            {
                if(dp[r-1][j]<MAX)
                {
                    tmp=j&s[r];//由于上次进位而产生的初始进位
                    for(k=tmp;k<(1<<N);k++)
                    {

                        if((k&tmp)==tmp&&/*规定了k的每一位进位必须大于或等于tmp的每一位*/
                           ((s[r]^j)&(k^tmp))==(k^tmp)&&/*规定了如果要继续进位成k则要求s[i]^j的那一位至少为1*/
                           ((t[(s[r]^j)^(k^tmp)]&1)==0||t[(s[r]^j)^(k^tmp)]<N))/*规定该进位下的1的个数为偶数,或者
                           为奇数但前提是个数少于n,因为可以任意补一个1使得为偶数,不影响进位*/
                           //前2个判断的共同依据:如果当前位进位后还是为0则不可能再进位,因为当前位加1也只为1,加2则可以归纳到高一位的判断中
                           {
                               dp[r][k]=min(dp[r][k],dp[r-1][j]+(t[k^tmp]+(t[(s[r]^j)^(k^tmp)]&1))*(1<<(r-1)));
                           }
                    }
                }
            }
        }
        int Min=MAX;
        for(j=0;j<(1<<N);j++)
        {
            if((s[j]&1)==0)
                Min=min(Min,dp[len][j]);
        }
        printf("%d\n",Min);
    }
    return 0;
}


标题基于Python的自主学习系统后端设计与实现AI更换标题第1章引言介绍自主学习系统的研究背景、意义、现状以及本文的研究方法和创新点。1.1研究背景与意义阐述自主学习系统在教育技术领域的重要性和应用价值。1.2国内外研究现状分析国内外在自主学习系统后端技术方面的研究进展。1.3研究方法与创新点概述本文采用Python技术栈的设计方法和系统创新点。第2章相关理论与技术总结自主学习系统后端开发的相关理论和技术基础。2.1自主学习系统理论阐述自主学习系统的定义、特征和理论基础。2.2Python后端技术栈介绍DjangoFlask等Python后端框架及其适用场景。2.3数据库技术讨论关系型和非关系型数据库在系统中的应用方案。第3章系统设计与实现详细介绍自主学习系统后端的设计方案和实现过程。3.1系统架构设计提出基于微服务的系统架构设计方案。3.2核心模块设计详细说明用户管理、学习资源管理、进度跟踪等核心模块设计。3.3关键技术实现阐述个性化推荐算法、学习行为分析等关键技术的实现。第4章系统测试与评估对系统进行功能测试和性能评估。4.1测试环境与方法介绍测试环境配置和采用的测试方法。4.2功能测试结果展示各功能模块的测试结果和问题修复情况。4.3性能评估分析分析系统在高并发等场景下的性能表现。第5章结论与展望总结研究成果并提出未来改进方向。5.1研究结论概括系统设计的主要成果和技术创新。5.2未来展望指出系统局限性并提出后续优化方向。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值