蓝桥杯 2019 省A 糖果 动态规划/二进制


 

#include <bits/stdc++.h> // 包含标准库中的所有头文件
using namespace std;

int main()
{
	int n,m,k; // 定义变量n(糖果包数)、m(口味数)、k(每包糖果的个数)
	cin>>n>>m>>k; // 输入n、m、k的值
	vector<int>dp(1<<m+1,100); // 定义动态规划数组dp,初始值为100,大小为2的(m+1)次方
	vector<int>v(1<<m+1,0); // 定义糖果口味数组v,初始值为0,大小为2的(m+1)次方
	
	for(int i=0;i<n;i++) // 遍历每一包糖果
	{
		int h=0,p;
		for(int j=1;j<=k;j++) // 遍历每包糖果中的每个口味
		{
			cin>>p; // 输入口味编号
			p-=1; // 将口味编号转换为数组索引(从0开始)
			h=h|(1<<p); // 将该口味对应的位标记为1,表示这个口味被买了
		}
		v[i]=h; // 将当前糖果的口味情况存入糖果口味数组中
		dp[h]=1; // 更新动态规划数组,表示只需要一包糖果就能满足这种口味需求
	}
	for(int i=0;i<(1<<m);i++) // 遍历所有可能的口味组合
	{
		for(int j=0;j<n;j++) // 遍历每一包糖果
		{
			dp[i|v[j]]=min(dp[i|v[j]],dp[i]+1); // 更新动态规划数组,尝试用当前口味组合i去购买第j包糖果,更新最小糖果包数
		}
	}
	if(dp[(1<<m)-1]==100) // 如果对应全口味的糖果包数为100,表示无法满足所有口味
	cout<<"-1"; // 输出-1
	else
	cout<<dp[(1<<m)-1];  // 输出满足所有口味的最小糖果包数
	return 0; // 返回0,表示程序正常结束
}

思路分析:

  1. 首先,定义了两个数组:dp数组用于存储达到某种口味组合所需的最小糖果包数,v数组用于存储每包糖果的口味情况。

  2. 遍历每一包糖果,对每包糖果中的每个口味进行标记,使用位运算将口味转换为对应的位标记。

  3. 根据每包糖果的口味情况,更新dp数组,表示只需要一包糖果就能满足该口味需求。

  4. 遍历所有可能的口味组合,尝试用当前口味组合去购买每一包糖果,并更新最小糖果包数。

  5. 最后,如果对应全口味的糖果包数为100,表示无法满足所有口味,输出-1;否则输出满足所有口味的最小糖果包数。

注:

  • dp数组:dp[i] 表示达到口味组合 i 所需的最小糖果包数。这里口味组合指的是一个二进制数,每一位表示对应口味是否被包含,如果某一位为1,则表示对应的口味被买了,为0则表示没有买。例如,如果有三种口味(m=3),那么口味组合 5(二进制 101)表示第1种口味和第3种口味被买了,第2种口味没有被买。初始时,dp数组的值均为100,表示无法达到对应的口味组合。

  • v数组:v[i] 表示第 i 包糖果的口味情况。它也是一个二进制数,每一位表示对应的口味是否在这包糖果里,如果某一位为1,则表示这个口味在这包糖果里,为0则表示不在。同样以三种口味为例,如果第 i 包糖果的口味组合为 6(二进制 110),则表示第2种口味和第3种口味在这包糖果里,第1种口味不在。

上一篇:2024-04-08