状压DP,是利用计算机二进制的性质来描述状态的一种DP方式,其特点是规模比较小,n一般小于15,而且一般只有两种决策,目的是防止爆空间
学习前需要先了解基本的位运算操作
然后我们来看一道例题:
题目:求每一种放法的背包价值
- 定义状态
状态应该是这n件物品的放与不放的情况
很容易想到的是开个n维数组,第i个维度的下标如果是1的话代表放第i件物品,0的话代表不放第i件物品
但是这样很容易造成空间浪费,而且多维数组也不好开
但如果用1和0代表放和不放,即用二进制表示所有物品的放与不放的情况,就只有一个维度了
所以我们定义用 dp[state] 表示此种请况下的背包价值
- 描述不同状态如何转移
放的状态只能从不放的状态转移过来,所以
dp[10000] = dp[00000] + W[1]
dp[11000] = dp[01000] + W[1] 或 dp[10000] + W[2]
…
- 代码实现
#include<bits/stdc++.h>
const int INF = 1 << 15;
int dp[INF + 10];
int dp1[INF+ 10] ; //定义状态
//打印在状态为num的时候的所有物品放与不放的情况
void print1(int num){
int k= 0;
if(num == 0) printf("0");
for(;(1 << k) <= num; k++){
if(num & (1<<k)) printf("1");
else printf("0");
}
}
int main(){
int n,m;
while(~scanf("%d%d",&n,&m)){
int W[20],C[20];
for(int i = 0; i < n; i++)
scanf("%d %d",&C[i],&W[i]) ;
int res = -1;
for(int i = 0; i < (1 << n); i++){
for(int j = 0; j < n; j++){
if(!(i&(1 << j))){ //状态转移
int temp = i | (1<<j);
dp[temp] = dp[i] + W[j];
dp1[temp] = dp1[i] + C[j];
}
}
}
for(int i = 0; i < (1 << n); i++){
print1(i);
//打印出每种方案的情况,价值和耗费的空间
printf("%d%d\n",dp[i],dp1[i]);
}
}
return 0;
}