題目描述
糖果店的老板一共有 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; }