題目描述:
【問題描述】 糖果店的老闆一共有 M 種口味的糖果出售。爲了方便描述,我們將 M 種 口味編號 1 ∼ M。 小明希望能品嚐到所有口味的糖果。遺憾的是老闆並不單獨出售糖果,而 是 K 顆一包整包出售。 幸好糖果包裝上註明了其中 K 顆糖果的口味,所以小明可以在買之前就知 道每包內的糖果口味。 給定 N 包糖果,請你計算小明最少買幾包,就可以品嚐到所有口味的糖 果。
【輸入格式】 第一行包含三個整數 N、M 和 K。 接下來 N 行每行 K 這整數 T1, T2, · · · , 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。
分析:
糖果口味M種,每包糖K種口味,共N包糖
則所有糖果口味加在一起,共有2^M種狀態
即從0000,0001,......,1111,從一種口味都沒有到所有口味全有
這個狀態用數組dp[1<<M]來記錄,1<<M代表1左移M位=2^M
用s[i]記錄每包糖果的狀態,例如10101,代表該包糖果能有1,3,5號口味
代碼如下:
#include<iostream>
using namespace std;
int n,m,k;
int main()
{
cin>>n;
cin>>m;
cin>>k;
int s[n+1];
int dp[1<<m];
for(int i=0;i<(1<<m);i++)
dp[i]=-1;
for(int i=1;i<=n;i++)
{
int ss=0;
int t;
for(int j=1;j<=k;j++)
{
//該循環中,用ss和或運算,來記錄該包糖果所能產生的狀態,並存入s[i]數組中
//同時用dp數組記錄下該包糖果的狀態
//dp[ss]=1 代表着該狀態可由一包糖果達到
cin>>t;
ss|=1<<(t-1);
}
s[i]=ss;
dp[ss]=1;
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<=(1<<m)-1;j++)
{
//判斷當前狀態是否可達到
//實際上用來跳過一些只有0或幾種口味的狀態,即跳過dp[0],dp[1]等
if(dp[j]==-1)continue;
//若該狀態可達
if(dp[j|s[i]]==-1)
//在該狀態dp[j]的基礎上,逐個篩選加上每個糖果後的狀態,如目標狀態未達到,則讓其達到
dp[j|s[i]]=dp[j]+dp[s[i]];
else
//若目標狀態已達到,則選取更小的
dp[j|s[i]]=min(dp[j|s[i]],dp[j]+dp[s[i]]);
}
}
cout<<dp[(1<<m)-1];
}