• 【解题报告】P1896 [SCOI2005]互不侵犯


    我闷今天的目的就是通过这道题初步理解一下状态压缩类动态规划

    首先我们先来介绍一下定义,所谓状态压缩类动态规划,顾名思义,这是以集合信息为状态的特殊的动态规划问题。主要有传统集合动态规划和基于连通性状态压缩的动态规划两种。

    因为某些动态规划的需求信息量非常的大,并且我们为每一个信息开一维数组这样的做法是非常不现实的,所以说我们的状态压缩类动态规划也就应运而生了!

    预备知识

    位操作是一种非常快的基本运算:有左移、右移、与、或、非等运算。

    左移:左移一位相当于某数乘2.

    右移:右移一位相当于某数除以2.

    与运算:按位进行“与”运算,两数同一位都为1时结果为1,否则为0,例如:101&110=100。

    或运算:按位进行“或”运算,两数同一位都为0时结果为0,否则为1,例如:101|110=111。

    非运算:按位取反。0变1,1变0,例如:~101=010。

    于是学会了上面这些操作,我们就可以干一些比较简单的事情了。

    • 判断第i位是否为0,(S&(1<<i))==0,意思是将1左移i位与S进行与运算后,看结果是否为0.

    • 将第i位设置为1,S|(1<<i),意思是将1左移i位与S进行或运算.

    • 将第i位设置为0,S&~(1<<i),意思是S与第i位为0,其余位为1的数进行与运算。

    例如:S=1010101,i=5.

    S&(1<<i):1010101&0100000=0000000.

    S|(1<<i):1010101|0100000=1110101.

    S&~(1<<i):1010101&1011111=1010101.

    这里在讲代码前我觉得还是有必要在科普一个知识,就是关于判断一个整数的二进制数里有几个1。这里我们需要用一个骚骚的操作。虽然我本人也没有看懂。

    int func(unsigned int n){
      int count=0;
      while(n>0){
        n=n&(n-1);
        count++;
      }
      return count;
    }
    

    这里转载一段:

    比如输入10,n-1=9,1010(10的二进制)&1001(9的二进制),得到一个1000(8的二进制),n=8,count++=1
    第二次循环,n-1=7,8&7=0,count++=2;跳出循环,返回count,也就是说10的二进制里面有2个1。。。。

    压行代码:

    LL func(LL n){LL count=0;while(n>0){n=n&(n-1);count++;}return count;}
    
    

    所以说大家应该也都懂了应该怎么写了!!!

    代码如下:

    #include<bits/stdc++.h>
    #define LL long long
    using namespace std;
    LL avai[2000],gs[2000],cnt=0,n,yong,f[10][2000][100],ans;
    LL func(LL n){LL count=0;while(n>0){n=n&(n-1);count++;}return count;}
    bool check(LL i){return (i&(i>>1))==0&&(i&(i<<1))==0;}
    int main()
    {
        scanf("%lld%lld",&n,&yong);
        for(LL i=0;i<(1<<n);++i)if(check(i))avai[++cnt]=i,gs[cnt]=func(i);//预处理所有的状态,avai记录的是状态,而ge记录的是当前状态所用的国王的个数 
        for(LL i=1;i<=cnt;i++)f[1][i][gs[i]]=1;//第一层的所有状态均是有1种情况的
        for(LL i=2;i<=n;i++)
    		for(LL k=1;k<=cnt;k++)
    			for(LL j=1;j<=cnt;j++){//枚举i、j、k,j,k在循环中没有特殊要求,反着写也可以 
            		if((avai[j]&avai[k])||((avai[j]<<1)&avai[k])||((avai[j]>>1)&avai[k]))continue;//排除不合法国王情况
            		for(LL s=yong;s>=gs[j];s--)f[i][j][s]+=f[i-1][k][s-gs[j]];
    			}//枚举s,计算f[i][j][s]
    	for(LL i=1;i<=cnt;i++)ans+=f[n][i][yong];
        printf("%lld",ans);
        return 0;
    }
    
  • 相关阅读:
    AC日记——[HNOI2008]GT考试 bzoj 1009
    AC日记——[SCOI2009]游戏 bzoj 1025
    AC日记——[HNOI2010]BOUNCE 弹飞绵羊 洛谷 P3203
    AC日记——旅游 bzoj 2157
    NOIP模拟2017.6.11解题报告
    AC日记——【模板】Link Cut Tree 洛谷 P3690
    AC日记——[SDOI2010]大陆争霸 洛谷 P3690
    [NOI2010]超级钢琴 倍增
    [HNOI2004]L语言 字典树 记忆化搜索
    对拍
  • 原文地址:https://www.cnblogs.com/mudrobot/p/13329121.html
Copyright © 2020-2023  润新知