问题描述
糖果店的老板一共有 M 种口味的糖果出售。为了方便描述,我们将 M 种口味编号 1 ∼ M。
小明希望能品尝到所有口味的糖果。遗憾的是老板并不单独出售糖果,而是 K 颗一包整包出售。
幸好糖果包装上注明了其中 K 颗糖果的口味,所以小明可以在买之前就知道每包内的糖果口味。
给定 N 包糖果,请你计算小明最少买几包,就可以品尝到所有口味的糖
果。
输入格式
第一行包含三个整数 N、M 和 K。
接下来 N 行每行 K 这整数 T₁, T₂, · · · , 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 ≤ Ti ≤ M。
1.二进制保存可以存在的不同口味组合 即状态压缩
i|st 就是把标记过的口味和新选糖果包st有的口味叠加起来,得到新的口味组合
2.状态转移
如果加一包糖后的组合第一次出现,说明没加这一包糖的时候dp[i] 再加1包 就能得到新组合 也就是 dp[i|st]=dp[i]+1;
如果 加一包糖得到的组合在之前的状态转移中有过了,
但是 糖的包数比现在组合糖的包数+1要多 那还是现在这个组合加1比较好,更新为 dp[i|st]=dp[i]+1;
#include<bits/stdc++.h>
using namespace std;
int dp[1<<20]; //得到一种方案包含的几种口味所需要的最少糖果包数量
int ST[100]; //ST[i]:第i包糖果的口味
int main() {
int n,m,k; cin>>n>>m>>k;
int tot=(1<<m)-1;
//tot:二进制是m个1,表示m种口味的所有组合
memset(dp, -1, sizeof dp);
for (int i=0; i<n; i++){
int st=0;
for (int j=0; j<k; j++){
int x;
cin>>x;
//用st记录同一包糖果内每多一颗糖果的口味组合
st|=(1<<x-1); //状态压缩
}
//此时无法体现最小 标记这一包的口味组合存在为1
dp[st]=1;
//记录下第i包糖果的口味组合
ST[i]=st;
}
//遍历所有口味组合,
for (int i=0; i<=tot; i++)
//如果遇到标记过的口味组合
if (dp[i]!=-1)
//检查给定的n包糖果
for (int j=0; j<n; j++) {
int st=ST[j];
//加一包糖后的组合第一次出现
//加一包糖得到的组合糖的包数比现在组合糖的包数+1要多
if (dp[i|st]==-1 || dp[i|st]>dp[i]+1) //状态转移
dp[i|st]=dp[i]+1;
}
cout << dp[tot];
//得到所有口味tot的最少糖果包数量
return 0;
}