zoukankan      html  css  js  c++  java
  • 【解题报告】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;
    }
    
  • 相关阅读:
    ETL利器Kettle实战应用解析系列三 【ETL后台进程执行配置方式】
    ETL利器Kettle实战应用解析系列二 【应用场景和实战DEMO下载】
    Kettle使用介绍
    java反射详解
    request详解
    java访问接口
    原生JS写Ajax的请求函数-原生ajax
    阿拉伯数字金额转换为大写
    深入理解Java中的String
    Strust2中,加入监听器来判断用户是否在session中存在。
  • 原文地址:https://www.cnblogs.com/mudrobot/p/13329121.html
Copyright © 2011-2022 走看看