[蓝桥杯 2019 省 A] 糖果
题目描述
糖果店的老板一共有 种口味的糖果出售。为了方便描述,我们将 种口味编号 ∼ 。
小明希望能品尝到所有口味的糖果。遗憾的是老板并不单独出售糖果,而是 颗一包整包出售。
幸好糖果包装上注明了其中 颗糖果的口味,所以小明可以在买之前就知道每包内的糖果口味。
给定 包糖果,请你计算小明最少买几包,就可以品尝到所有口味的糖果。
输入格式
第一行包含三个整数 、 和 。
接下来 行每行 这整数 ,代表一包糖果的口味。
输出格式
一个整数表示答案。如果小明无法品尝所有口味,输出 。
样例 #1
样例输入 #1
6 5 3
1 1 2
1 2 3
1 1 3
2 3 5
5 4 2
5 1 2
样例输出 #1
2
提示
对于 的评测用例,。
对于所有评测样例,,,,。
蓝桥杯 2019 年省赛 A 组 I 题。
思路
首先,定义一个数组 candy,数组的每个元素都是一个位集,表示每个糖果包中糖果的口味。然后,定义一个数组 dp,数组的每个元素表示对应的口味状态下最少需要购买的糖果包的数量。初始化 dp 数组的所有元素为 INF,表示无法达到对应的口味状态。
接着,从输入中读取糖果包的数量 n,糖果的口味数量 m,每个糖果包中糖果的数量 k。对于每个糖果包,从输入中读取 k 个糖果的口味,然后更新 candy 数组的相应元素。
然后,进行动态规划。状态转移方程为:
其中, 是当前的口味状态, 是通过购买某个糖果包从 状态转移到的新的口味状态。如果可以通过购买一个糖果包从 状态转移到 状态,那么就更新 状态下最少需要购买的糖果包的数量。
对于每个糖果包和每个口味状态,如果当前的口味状态可以通过购买当前的糖果包来达到,那么就更新 dp 数组的相应元素。具体来说,如果 dp[j] 的值不大于 100,那么就用位运算符 | 来得到新的口味状态 k,然后用 min 函数来更新 dp[k] 的值。
最后,输出 dp 数组中表示所有口味的元素的值。如果这个值等于 INF,那么就输出 -1,表示无法品尝所有口味;否则就输出这个值,表示最少需要购买的糖果包的数量。
AC代码
#include <algorithm>
#include <bitset>
#include <cmath>
#include <cstring>
#include <iostream>
#define AUTHOR "HEX9CF"
using namespace std;
using ll = long long;
const int N = 1e2 + 7;
const int M = 20;
const ll L = (1 << M);
const int INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;
// n包,m种,每包k颗
int n, m, k;
// i状态最少需要的包数
int dp[L];
bitset<M> candy[N];
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
memset(dp, INF, sizeof(dp));
cin >> n >> m >> k;
for (int i = 0; i < n; i++) {
candy[i].reset();
for (int j = 0; j < k; j++) {
int t;
cin >> t;
candy[i][t - 1] = 1;
}
// cout << candy[i] << endl;
}
dp[0] = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < (1 << m); j++) {
bitset<M> t(j);
if (dp[j] > 100) {
continue;
}
int k = (t | candy[i]).to_ullong();
// cout << k << endl;
dp[k] = min(dp[k], dp[j] + 1);
}
}
cout << ((dp[(1 << m) - 1] == INF) ? -1 : dp[(1 << m) - 1]) << endl;
return 0;
}