状压DP——一种用状态压缩优化的DP(光棍组织)

状态压缩,是一种将多个状态压缩成一个数的技巧.

例如一个数组a[2][2][2][2][2][2],我们可以将其压缩成a[64]表示.

那么在a数组中访问a[x1][x2][x3][x4][x5][x6]就可以缩减成a[x1<<5+x2<<4+x3<<3+x4<<2+x5<<1+x6<<0].

这样做有时可以更加方便.

那么我们要如何来访问某一位例如第x位上的状态呢?

由于是二进制,我们知道它的二进制后看看第x位是否为1.

怎么看呢?我们可以用&运算符.

&的运算符是按位与运算,那么我们要判断第x为1还是0,根据与运算的定义,都为1则为1,否则为0,所以判断第x为是否为0,于是我们让这个数与第x位为1,而其它位都为0的数进行&运算,然后看这个数是否为0就好了.

也就是说我们可以这样实现:

bool exist(int s,int x){      //判断s的第x位是否为1 
  if (s&1<<x) return true;    //若s的第x位为1,返回true 
  else return false;          //否则返回false 
}

于是我们让第x位从1变为0就是把第x位单独取出,将原数减去表示第x位的数即可:

int del(int s,int x){
  return s-1<<x;      //将原数减去第x为表示的数 
}

至于如何去枚举S的所有子集,我们可以采用如下循环:

for (j=S;j;j=S&(j-1))

基本这就是状压之后如何取一位的状态.

所以状压DP最重要的就是这个如何处理.

例如光棍组织一题:对于这 n 个光棍的任意一个组合,都有一个被称为“和谐度”的东西,现在,他们想知道, 如何分组可以使和谐度总和最大.

那么题目的输入按照二进制组合的方式将每个组合的和谐度输入.

那么我们用f[i]表示二进制是i时的最大和谐度.

我们就可以枚举它的子集,并把另一个子集求出,合并来得到最大值.

代码如下:

#include<bits/stdc++.h>
  using namespace std;
const int N=16;
int f[1<<N+1]={0};
int n;
inline void into(){
  scanf("%d",&n);
  n=1<<n;
  for (int i=1;i<n;i++)
    scanf("%d",&f[i]);      //将直接的和谐度输入作为初始化 
}
inline void work(){
  for (int i=1;i<n;i++)      //枚举每个状态 
    for (int j=i&(i-1);j;j=i&(j-1))      //枚举子集 
      f[i]=max(f[i],f[j]+f[i-j]);      //状态转移 
}
inline void outo(){
  printf("%d\n",f[n-1]);
}
int main(){
  into();
  work();
  outo();
  return 0;
}
差不多就这样了.

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/79307919
今日推荐