题目如下:
晓萌作为一个营养学专家,吃东西的时候总是要考虑是否能满足他的各种营养物质需求,大家都说他是一个有强迫症的吃货。晓萌知道每种食品中营养物质的含量,请你帮他安排食谱,以保持他获得所需营养物质的同时,吃的食品的种类最少。(营养物质含量为整数,每种食品晓萌只吃一次)。
输入第1行为一个整数V表示晓萌需要的营养物质的种类数(1≤V≤25)。
输入第2行包括用空格分隔开的V个整数,依次表示每种营养物质的需求量(1≤每个整数≤1000)。
输入第3行包括一个整数G表示提供可以给晓萌吃的食品的种类数(1≤G≤15)。
接下来的G行,每行包括用空格分隔开的V个整数,G行中的第i行中V个整数依次表示第i种食品中每种营养物质的含量(1≤每个整数≤1000)。
输出包括空格分隔的多个数,第一个数为必需的最小的食品种数P;后面有P个数,表示所选择的食品编号(按从小到大排列);如果有多个解,输出食品序号最小的(即字典序最小)。
样例输入·
4
100 200 300 400
3
50 50 50 50
200 300 200 300
900 150 389 399
样例输出·
2 1 3
首先说一下思路吧
- 首先,如果不明白 字典序 含义的话,自行百度
- 首要条件是 所吃食品种类越少越好,因此直接枚举食品种类为 1、2、3……G 的情况,以减少遍历次数;
- 在前一思路之下,当前的关键点在于避免出现1、2、3、4 和 1、2、4、3 这样的重复查询(因为吃食品的次序不影响结果),否则会超时;即在递归过程中,若第 cur 种食品已经吃过后,只需要考虑序号在 cur 之后的食品
- 最后考虑字典序,因为寻找次序是从食品种类为 1 开始递增,并且吃食品的顺序是从序号小的食品开始,因此只要找到第一个满足条件的序列,必然是食品种类最少,并且字典序最小的序列。
附上代码
#include<iostream>
using namespace std;
struct Food{
int f[25];
};
int V,G;
//用来标记吃过的食品
int map[16]={0};
//记录需要的各种营养物质含量
int need[25];
//记录当前吃过的食品各营养物质总量
int prov[25];
Food food[16];
//标记是否已经找到满足要求的食品
bool m;
//记录当前食品种类
int kind;
//本层递归要吃第step种食品,前一层递归所吃食品序号为cur
void dfs(int step,int cur)
{
//首先判断是否已经找到满足条件的食品序列
m = true;
for(int k=0;k<V;k++)
{
if(prov[k]<need[k])
{
m = false;
break;
}
}
if(m==true)
return;
//判断本次递归是否吃够 kind种食品
if(step>kind)
return;
//这里要明白,在前一层递归,已经吃掉了序号为cur的食品
//因此,不需要再吃序号在cur之前的食品,否则会出现1、2、3、4和1、2、4、3这样的重复序列
for(int i=cur;i<=G;i++)
{
if(map[i]==0)
{
for(int j=0;j<V;j++)
prov[j]+=food[i].f[j];
map[i]=1;
dfs(step+1,i);
//若已经找到符合条件的序列,便一层层退出递归
if(m==true)
return;
//将已吃食品“吐出来”
map[i]=0;
for(int j=0;j<V;j++)
prov[j]-=food[i].f[j];
}
}
return;
}
int main()
{
cin>>V;
for(int i=0;i<V;i++)
cin>>need[i];
cin>>G;
for(int i=1;i<=G;i++)
for(int j=0;j<V;j++)
cin>>food[i].f[j];
int i;
for(i=1;i<=G;i++)
{
kind = i;
if(m==true)
break;
dfs(1,1);
}
cout<<i-1;
for(int i=1;i<=G;i++)
{
if(map[i]==1)
cout<<' '<<i;
}
cout<<endl;
return 0;
}
平时写博客不多,但是刷题过程中难免遇到一些有意思的题目,因此想要将自己的代码分享出来,希望同样做过这道题目的大神可以留言指出代码中的不足,困惑于此题的人也可以留言道出,以此相互学习,共同精进。
思路参考
http://blog.csdn.net/ldw201510803006/article/details/72594317
在参考这篇博客之前困于超时,参考了这篇博客之后根据自己的理解终于通过了。