[蓝桥杯][2019年第十届真题]糖果*(状压dp)
题目描述
糖果店的老板一共有 M 种口味的糖果出售。为了方便描述,我们将 M 种 口味编号 1 ∼ M。
小明希望能品尝到所有口味的糖果。遗憾的是老板并不单独出售糖果,而 是 K 颗一包整包出售。
幸好糖果包装上注明了其中 K 颗糖果的口味,所以小明可以在买之前就知 道每包内的糖果口味。
给定 N 包糖果,请你计算小明最少买几包,就可以品尝到所有口味的糖 果。
输入
第一行包含三个整数 N、M 和 K。
接下来 N 行每行 K 这整数 T1, T2, · · · , TK,代表一包糖果的口
输出
一个整数表示答案。如果小明无法品尝所有口味,输出 −1。
样例输入
6 5 3 1 1 2 1 2 3 1 1 3 2 3 5 5 4 2 5 1 2
样例输出
2
提示
对于30%的评测用例,1 ≤ N ≤ 20。
对于所有评测样例,1≤N≤100,1≤M≤20,1≤K≤20,1
思路
- 用二进制的形式表示每包糖果的口味分布,以第一个样例举例:
- 1 1 2 --> 1 1 0 0 0
- 1 2 3 --> 1 1 1 0 0
- 1 1 3 --> 1 0 1 0 0
- 2 3 5 --> 0 1 1 0 1
- 5 4 2 --> 0 1 0 1 1
- 1 5 2 --> 1 1 0 0 1
- 用一个map记录糖果组合下来的口味分布:口味-包数
- 遍历每一种口味,如果这个口味在该组合中没有,那就加上一包含有这个口味的糖果
- 从相同的口味分布中取得包数最少的那一组,然后继续遍历。
- flag记录是否存在组合
代码
#include<iostream> #include<map> using namespace std; /* 6 5 3 1 1 2 1 2 3 1 1 3 2 3 5 5 4 2 5 1 2 */ int main() { int n, m, k; int pc[105]; cin >> n >> m >> k; for (int i = 0; i < n; i++) { // 状态压缩 int pack = 0; for (int j = 0; j < k; j++) { int c; cin >> c; c--; pack |= (1 << c); } pc[i] = pack; } map<int, int>mc; mc[0] = 0; bool flag; for (int i = 0; i < m; i++) { // 对每一种口味 map<int, int>t; flag = false; for (auto p : mc) { // 对每一种组合(口味-->包数) int a = p.first, b = p.second; if ((1 << i)&a) { // 如果这个组合中有这个口味 flag = true; if (t.count(a)) // // 这个组合存在 t[a] = t[a] < b ? t[a]: b; else t[a] = b; } else { // 如果没有这个口味 for (int j = 0; j < n; j++) { // 对每一包糖果 if (pc[j] & (1 << i)) { // 如果该包糖果有这个口味,就加入组合 flag = true; if (t.count(a | pc[j])) // 这个口味分布存在 t[a | pc[j]] = t[a | pc[j]] < b + 1 ? t[a | pc[j]] : b + 1; else t[a | pc[j]] = b + 1; } } } } if (!flag) break; mc = t; } if (flag) { // 若存在,则遍历所有组合,输出最小包数 int res = 1 << 30; for (auto p : mc) { res = res<p.second?res:p.second; } cout << res; } else cout << "-1"; return 0; }