码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2020-07-17 13:54:46      阅读:60      评论:0      收藏:0      [点我收藏+]

标签:返回   printf   左移   fine   定义   维数   否则   --   目的   

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

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

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

预备知识

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

左移:左移一位相当于某数乘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;
}

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

标签:返回   printf   左移   fine   定义   维数   否则   --   目的   

原文地址:https://www.cnblogs.com/mudrobot/p/13329121.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!