【洛谷 P8687】[蓝桥杯 2019 省 A] 糖果 题解(动态规划+位集合+位运算)

[蓝桥杯 2019 省 A] 糖果

题目描述

糖果店的老板一共有 M M M 种口味的糖果出售。为了方便描述,我们将 M M M 种口味编号 1 1 1 M M M

小明希望能品尝到所有口味的糖果。遗憾的是老板并不单独出售糖果,而是 K K K 颗一包整包出售。

幸好糖果包装上注明了其中 K K K 颗糖果的口味,所以小明可以在买之前就知道每包内的糖果口味。

给定 N N N 包糖果,请你计算小明最少买几包,就可以品尝到所有口味的糖果。

输入格式

第一行包含三个整数 N N N M M M K K K

接下来 N N N 行每行 K K K 这整数 T 1 , T 2 , ⋯   , T K T_1,T_2, \cdots ,T_K T1,T2,,TK,代表一包糖果的口味。

输出格式

一个整数表示答案。如果小明无法品尝所有口味,输出 − 1 -1 1

样例 #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

提示

对于 30 % 30\% 30% 的评测用例, 1 ≤ N ≤ 20 1 \le N \le 20 1N20

对于所有评测样例, 1 ≤ N ≤ 100 1 \le N \le 100 1N100 1 ≤ M ≤ 20 1 \le M \le 20 1M20 1 ≤ K ≤ 20 1 \le K \le 20 1K20 1 ≤ T i ≤ M 1 \le T_i \le M 1TiM

蓝桥杯 2019 年省赛 A 组 I 题。


思路

首先,定义一个数组 candy,数组的每个元素都是一个位集,表示每个糖果包中糖果的口味。然后,定义一个数组 dp,数组的每个元素表示对应的口味状态下最少需要购买的糖果包的数量。初始化 dp 数组的所有元素为 INF,表示无法达到对应的口味状态。

接着,从输入中读取糖果包的数量 n,糖果的口味数量 m,每个糖果包中糖果的数量 k。对于每个糖果包,从输入中读取 k 个糖果的口味,然后更新 candy 数组的相应元素。

然后,进行动态规划。状态转移方程为:

d p [ i ] = min ⁡ ( d p [ i ] , d p [ j ] + 1 ) dp[i] = \min(dp[i], dp[j] + 1) dp[i]=min(dp[i],dp[j]+1)

其中, j j j 是当前的口味状态, i i i 是通过购买某个糖果包从 j j j 状态转移到的新的口味状态。如果可以通过购买一个糖果包从 j j j 状态转移到 i i i 状态,那么就更新 i i i 状态下最少需要购买的糖果包的数量。

对于每个糖果包和每个口味状态,如果当前的口味状态可以通过购买当前的糖果包来达到,那么就更新 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;
}

  • 25
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值