2021年寒假每日一题,2017~2019年的省赛真题。本文内容由倪文迪(华东理工大学计算机系软件192班)和罗勇军老师提供。每日一题,关注蓝桥杯专栏: https://blog.csdn.net/weixin_43914593/category_10721247.html
文章目录
2019省赛A组第9题“糖果” ,题目链接:
http://oj.ecustacm.cn/problem.php?id=1460
1、题目描述
糖果店的老板一共有M 种口味的糖果出售。为了方便描述,我们将M种口味编号1~M。
小明希望能品尝到所有口味的糖果。遗憾的是老板并不单独出售糖果,而是K颗一包整包出售。
幸好糖果包装上注明了其中K 颗糖果的口味,所以小明可以在买之前就知道每包内的糖果口味。
给定N 包糖果,请你计算小明最少买几包,就可以品尝到所有口味的糖果。
输入:第一行包含三个整数N、M 和K。
接下来N 行每行K 这整数T1,T2,…,TK,代表一包糖果的口味。
1<=N<=100,1<=M<=20,1<=K<=20,1<=Ti<=M。
输出:一个整数表示答案。如果小明无法品尝所有口味,输出-1。
2、题解
2.1 暴力
先想想暴力法:对 n n n包糖果做任意组合,找到其中一种组合,能覆盖所有口味,并且需要的糖果包数量最少。 n n n包糖果的组合共有 2 n 2^n 2n种,而 n n n=100,显然会严重超时。
2.2 状态压缩DP
本题很容易想到用DP来做。
(1)定义状态 d p [ i ] dp[i] dp[i],表示得到口味组合 i i i所需要的最少糖果包数量。
(2)状态如何转移?往口味组合 i i i中加入一包糖果,设得到新的口味组合 j j j,说明从 i i i到 j j j需要糖果包数量 d p [ i ] + 1 dp[i]+1 dp[i]+1。若原来的 d p [ j ] 大 于 d p [ i ] + 1 dp[j]大于dp[i]+1 dp[j]大于dp[i]+1,说明原来得到 j j j的方法不如现在的方法,更新 d p [ j ] = d p [ i ] + 1 dp[j]=dp[i]+1 dp[j]=dp[i]+1。
这里关键的问题是如何表示口味组合。学过状态压缩DP的队员都知道,像题目中只有小规模的m=20这种应用,用状态压缩的二进制数来表示口味是很简单的。
例如一包里面有3颗糖果,分别是“2,3,5”三种口味,用二进制数“10110”表示,二进制数的每一位表示一种口味。
状态压缩DP的原理和扩展学习,参考博文https://blog.csdn.net/weixin_43914593/article/details/106432695
注意博文中这句话:“这样概况状态压缩DP的思想:集合的状态(子集或排列),如果用二进制表示状态,并用二进制的位运算来遍历和操作,又简单又快。当然,由于集合问题是NP问题,所以状态压缩DP的复杂度仍然是指数的,只能用于小规模问题的求解。”
下面给出C++代码,代码中详细解释了状态压缩的表示和DP转移。总复杂度是 O ( n 2 m ) O(n2^m) O(n2m),当n=100,m=20时,循环约1亿次,提交到OJ,勉强AC。
//改写自:User: 20192131006 Time:550 ms Memory:6216 kb
#include<bits/stdc++.h>
using namespace std;
int dp[1<<20]; //dp[v]:得到口味为v时需要的最少糖果包数量
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|=(1<<x-1); //状态压缩
}
dp[st]=1; //dp[v]:得到口味为v时需要的最少糖果包数量
ST[i]=st; //ST[i]:第i包糖果的口味
}
for (int i=0; i<=tot; i++) //遍历所有口味组合
if (dp[i]!=-1) //已存在得到口味i的最少糖果包数量
for (int j=0; j<n; j++) {
//检查给定的n包糖果
int st=ST[j];
if (dp[i|st]==-1 || dp[i|st]>dp[i]+1) //状态转移
dp[i|st]=dp[i]+1;
}
cout << dp[tot]; //得到所有口味tot的最少糖果包数量
return 0;
}
本来想加上Python代码的,但是总所周知,Python的for循环很慢,本题有1亿次循环,还是算了。