糖果店的老板一共有 M 种口味的糖果出售。
为了方便描述,我们将 M 种口味编号 1∼M。
小明希望能品尝到所有口味的糖果。
遗憾的是老板并不单独出售糖果,而是 K 颗一包整包出售。
幸好糖果包装上注明了其中 K 颗糖果的口味,所以小明可以在买之前就知道每包内的糖果口味。
给定 N 包糖果,请你计算小明最少买几包,就可以品尝到所有口味的糖果。
输入格式
第一行包含三个整数 N,M,K。
接下来 N 行每行 K 这整数 T1,T2,⋅⋅⋅,TK,代表一包糖果的口味。
输出格式
一个整数表示答案。
如果小明无法品尝所有口味,输出 −1。
数据范围
1≤N≤100,
1≤M,K≤20,
1≤Ti≤M
输入样例:
6 5 3
1 1 2
1 2 3
1 1 3
2 3 5
5 4 2
5 1 2
输出样例:
2
重复覆盖模型 迭代式递归 + 估值函数
#include<iostream>
#include<vector>
using namespace std;
int n,m,k,c;
vector<int> col[110];
int log2[1<<20];
int lowbit(int x){ // 返回最低位,如 x = 1010,则返回二进制的10即为十进制的2
return x & -x;
}
int h(int state){ // 估值函数
int res = 0;
for (int i = (1 << m) - 1 - state; i ; i -= lowbit(i)){
int c = log2[lowbit(i)]; // 最右边一列没有被覆盖的列号
res++;
for (auto x:col[c]) // col[c]中1代表的是覆盖了,0代表没覆盖,而i中1代表没覆盖,0代表覆盖了,因此x先取个反
i = i & ~x;
}
return res;
}
bool dfs(int depth,int state){
if (!depth || h(state) > depth) return state == (1 << m) - 1; // 1 << m - 1 和(1 << m) - 1 是不同的, 减号的优先级高于移位运算符
int t = -1;
for (int i = (1 << m) - 1 - state; i; i -= lowbit(i)){ // 减去了state,则state中0、1就互换了,如果某一位是1,则代表的是该列还没有被覆盖
c = log2[lowbit(i)]; // 获得最右边一列的列号
if (t == -1 || col[t].size() > col[c].size()) // 先去找被覆盖次数最少的行
t = c;
}
for (auto x:col[t]) // for循环枚举能覆盖第t列的每一行x, col[t]中存放的是能填满该列的每一行的state值
if (dfs(depth - 1,state | x))
return true;
return false;
}
int main(){
cin>>n>>m>>k;
for (int i = 0; i < m; ++i) log2[1<<i] = i; // 方便由1<<m得到是
for (int i = 0; i < n; ++i){
int state = 0;
for (int j = 0; j < k; ++j){ // 记录每一行能覆盖哪些列,用二进制的1表示
cin>>c;
state = state | (1 << c - 1);
}
for (int j = 0; j < m; ++j){ // 一共m种糖果,每一列将能将其覆盖的行的state值加入进去
if (state >> j & 1)
col[j].push_back(state);
}
}
int depth = 1;
while(depth <= m && !dfs(depth,0)) depth++; // while()就是迭代加深操作
if (depth > m) cout<<-1;
else cout<<depth;
return 0;
}