#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,表示程序正常结束
}
思路分析:
-
首先,定义了两个数组:dp数组用于存储达到某种口味组合所需的最小糖果包数,v数组用于存储每包糖果的口味情况。
-
遍历每一包糖果,对每包糖果中的每个口味进行标记,使用位运算将口味转换为对应的位标记。
-
根据每包糖果的口味情况,更新dp数组,表示只需要一包糖果就能满足该口味需求。
-
遍历所有可能的口味组合,尝试用当前口味组合去购买每一包糖果,并更新最小糖果包数。
-
最后,如果对应全口味的糖果包数为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种口味不在。