资源限制
时间限制:1.0s 内存限制:256.0MB
问题描述
糖果店的老板一共有 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。
数据范围肯定是不支持暴力dfs的,但是可以在此提供一下简单的思路。对于每一包糖果只有要与不要两种可能,那就分别进行dfs,取两者较小值即可。
#include<iostream>
#define inf 1000000
using namespace std;
int m,n,k,cnt=inf;
int t[140][40],vis[50];
void dfs1(int col,int x) //dfs1代表需要第col包糖果,dfs2表示不要第col包糖果。
{
for(int i=1;i<=k;i++)
vis[t[col][i]]+=col;
}
void dfs2(int col,int x)
{
for(int i=1;i<=k;i++)
vis[t[col][i]]-=col;
}
void dfs(int col,int x) //col表示第col糖果,x表示已经拿到了x包糖果。
{
if(x>=cnt)return; //若x比已知最优解还要大,说明肯定不可能再成为答案,直接return
int flag=1;
for(int i=1;i<=m;i++)
if(vis[i]<=0){flag=0;break;}
if(col==n&&!flag)return;
if(flag)
{
cnt=min(cnt,x);
return;
}
dfs1(col,x); //需要col包糖果
dfs(col+1,x+1);
dfs2(col,x); //不需要col包糖果
dfs(col+1,x);
}
int main()
{
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
for(int j=1;j<=k;j++)
cin>>t[i][j];
dfs(0,0);
if(cnt==inf)cout<<-1;
else cout<<cnt;
return 0;
}
显然会超时的QAQ,评测结果如上图所示。
看到数据范围20以内也提醒了我们可以用状压dp,详见注释。
sta可以用来存储状态,例如拿第1,2,5包,则可以用二进制表示为10011(1代表拿,0代表不拿),则下面的 j | sta[i] 就可以表示为两种状态的合成(比如第j种状态是11001,sta[i]为10011,则 j | sta[i]则为11011,就是拿1、2、4、5包)
#include<iostream>
#include<string.h>
using namespace std;
int dp[(1<<20)+20],t[120][50],sta[200];
int n,m,k;
int main()
{
cin>>n>>m>>k;
memset(dp,-1,sizeof(dp));
dp[0]=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=k;j++)
{
cin>>t[i][j];
sta[i]|=1<<(t[i][j]-1);
}
dp[sta[i]]=1; //至少买一包,储存每种可以买的状态
}
for(int i=1;i<=n;i++) //枚举每一包
for(int j=0;j<(1<<m);j++)//枚举所有状态
{
if(dp[j]==-1) continue; //若没有该状态直接跳过
if(dp[j|sta[i]]==-1||dp[j]+1<dp[j|sta[i]])
dp[j|sta[i]]=dp[j]+1; //第j种状态未出现过,则取两者较小值
}
cout<<dp[(1<<m)-1];
}
这样就可以成功AC啦~