状态压缩,是一种将多个状态压缩成一个数的技巧.
例如一个数组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; }差不多就这样了.