【备战蓝桥杯】5.糖果——状态压缩dp与二维变一维

304 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

题目描述

问题描述

  糖果店的老板一共有 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。

思路

一道比较典型的状态压缩的题(说是比较是因为我一时竟然还不太会做)。

什么叫状态压缩?就是如果有一组状态,每个状态可以抽象为0和1,就可以把这组状态转换为唯一对应的二进制数字。

以这道题举例,对于任何口味,0代表没有品尝到,1代表品尝到。假如一共有20种口味,那么口味从1到20,用一个二进制数字的每一位代表。这样从00(1<<20)1(1<<20)-1,每个数字代表唯一的一个状态,我们要求的就是(1<<20)1(1<<20)-1的状态下最少买几包。

设二维状态dp(i,j)代表只买前i包的情况下达到状态i最少买几包。(若不可能达到就是无穷大) 则状态转移方程:

dp(i,jtaste(i))=min(dp(i1,j)+1,dp(i1,jtaste(i)))dp(i,j|taste(i))=min( dp(i-1,j)+1,dp(i-1,j|taste(i)) )

不难发现,可以将dp函数的第一维去掉,这样可以节省内存空间。详情看代码即可,点到即止。

代码

#include<bits/stdc++.h>
using namespace std;
const int maxm=1<<20+5;
int dp[maxm];
const int maxn=100+5;
const int inf=0x3f3f3f3f;
int arr[maxn];
int main(){
	int n,m,k;
	cin>>n>>m>>k;
	memset(dp,0x3f3f3f3f,sizeof(dp));
	for(int i=0;i<n;i++){
		for(int j=0;j<k;j++){
			int a;cin>>a;
			arr[i]=arr[i]|(1<<(a-1));
		}
		dp[arr[i]]=1;
	}
	
	for(int j=0;j<n;j++){
		for(int i=0;i<(1<<m);i++){
			dp[i|arr[j]]=min(dp[i]+1,dp[i|arr[j]]);
			if(dp[i|arr[j]]>0x3f3f3f3f){
				dp[i|arr[j]]=inf;
			}
		}
	}
	cout<<(dp[(1<<m)-1]==inf?-1:dp[(1<<m)-1]);
	return 0;
	
}

总结

蓝桥杯对于动态规划考察的还是比较深的,以后要多多复习一下动态规划了。