状态压缩DP是什么?
答:利用位运算(位运算比加减乘除都快!)来记录状态,并实现动态规划。
适用于什么问题?
答:数据规模较小;不能使用简单的算法解决。
例题:
题目描述
糖果店的老板一共有M 种口味的糖果出售。为了方便描述,我们将M种口味编号1~M。
小明希望能品尝到所有口味的糖果。遗憾的是老板并不单独出售糖果,而是K颗一包整包出售。
幸好糖果包装上注明了其中K 颗糖果的口味,所以小明可以在买之前就知道每包内的糖果口味。
给定N 包糖果,请你计算小明最少买几包,就可以品尝到所有口味的糖果。
小明希望能品尝到所有口味的糖果。遗憾的是老板并不单独出售糖果,而是K颗一包整包出售。
幸好糖果包装上注明了其中K 颗糖果的口味,所以小明可以在买之前就知道每包内的糖果口味。
给定N 包糖果,请你计算小明最少买几包,就可以品尝到所有口味的糖果。
输入
第一行包含三个整数N、M 和K。
接下来N 行每行K 这整数T1,T2,...,TK,代表一包糖果的口味。
1<=N<=100,1<=M<=20,1<=K<=20,1<=Ti<=M。
接下来N 行每行K 这整数T1,T2,...,TK,代表一包糖果的口味。
1<=N<=100,1<=M<=20,1<=K<=20,1<=Ti<=M。
输出
一个整数表示答案。如果小明无法品尝所有口味,输出-1。
数据规模不大,很适合用状态压缩DP。
思路如下:
用二进制的1和0来表示某类糖果的有无。比如按照上面的样例,总共有5种糖果。第二行1 1 2就是00011,第三行1 2 3就是00111,最后一行5 1 2就是10011...
然后我们用dp数组在存储这些状态。
int dp[1<<m];
将其初始化为-1.每读取一行,就将相对应的元素赋值1,代表买一包就能买到这些种类的糖果。
AC代码如下:
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 int main() { 5 int n, m, k,s,ss; 6 cin >> n >> m >> k; 7 int dp[1 << 20]; 8 int goods[100]; 9 memset(dp, -1, sizeof(dp)); 10 for(int j=0;j<n;j++) { 11 ss = 0; 12 for (int i = 0; i < k; i++) { 13 cin >> s; 14 ss |= (1 << (s - 1)); 15 } 16 goods[j] = ss; 17 dp[ss] = 1; 18 } 19 for (int i = 0; i < n; i++) { 20 for (int j = 0; j < (1 << m); j++) { 21 if (dp[j] == -1) continue; 22 if (dp[j | goods[i]] == -1) 23 dp[j | goods[i]] = dp[j] + dp[goods[i]]; 24 else 25 dp[j | goods[i]] = min(dp[j] + dp[goods[i]], dp[j | goods[i]]); 26 } 27 } 28 cout << dp[(1 << m) - 1]; 29 }