试题 I: 糖果 时间限制: 1.0s 内存限制: 256.0MB 本题总分:25 分
【问题描述】
糖果店的老板一共有 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。
暴力是解决不了问题的!
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <vector>
#include <queue>
#include <algorithm>
using namespace std;
vector<int>v[105],path; //v存糖果,path为dfs时生成的路径
int visit[105],mp[105]; //visit标记糖果,mp标记口味
int N,M,K; //N为糖果包数,M为口味数,K为每包糖果含有的糖果数
int ans=9999;
void dfs(int n){
if(n>ans||path.size()<M&&n==N)return; //剪枝,如果当前取的糖果包数n比已知最小的解大,就没有必要继续走下去了,直接剪枝;当n==N说明取了N包糖果还没有解同样剪枝
if(path.size()==M){ //满足M中口味糖果
if(n<ans)ans=n; //如果当前的解比ans小,则更新ans=n
return; //该路径满足一组解是就可以剪枝了
}
for(int i=0;i<N;i++){ //遍历每一包糖果
if(visit[i]==0){ //如果第i包还没取过
for(int j=0;j<K;j++){ //遍历第i包里面所有的糖果
if(mp[v[i][j]]==0){ //如果口味v[i][j]还没有
path.push_back(v[i][j]);//将口味v[i][j]加到path中
mp[v[i][j]]=1; //标记糖果v[i][j]有了
}
}
n++; //目前已取的糖果包数+1
visit[i]=1; //标记第i包糖果已取
dfs(n); //继续递归
n--; //回溯,标记为原来的状态
visit[i]=0; // 回溯,标记为原来的状态
for(int j=K-1;j>=0;j--){ //回溯,因为上面取糖果的时候是从前往后取的,所以从后往前删(参考栈的操作)
if(path.back()==v[i][j]){
path.pop_back(); //回溯,把糖果放回去
mp[v[i][j]]=0; // 回溯,标记为原来的状态
}
}
}
}
}
int main(){
cin>>N>>M>>K;
for(int i=0;i<N;i++){
for(int j=0;j<K;j++){
int x;
scanf("%d",&x);
v[i].push_back(x);
}
}
dfs(0);
if(ans==9999)ans=-1;//如果没有解,则ans还是9999,-1表示解不存在
cout<<ans; //打印结果
return 0;
}