题意:
有若干个物品,每个物品有两种费用 和一个价值 ,
现在给你两个数字 和 ,每种物品可以选也可以不选,让你找到一种组合物品的方案,使得 并且 且 最小。
方法一(暴力DFS,不可AC)
依次递归地考虑每个物品 选/不选,最后统计符合要求的答案求最小值即可。
具体的,我们设递归函数 表示当前考虑到第 个物品,当前的 总和为 ,当前的 总和为 ,当前的 总和为 ,最后统计即可。
代码:
class Solution { public: int n;//物品总数 int x,y; int ans;//答案 vector<vector<int> > datas;//物品数据 void dfs(int i,int j,int k,int v){ if(i>=n){ //n个物品全部决策完毕 if(j>=x&&k>=y)ans=min(ans,v); return; } dfs(i+1,j+datas[i][0],k+datas[i][1],v+datas[i][2]);//选 dfs(i+1,j,k,v);//不选 } int minCost(int breadNum, int beverageNum, vector<vector<int> >& packageSum) { this->n=packageSum.size(); this->x=breadNum; this->y=beverageNum; this->ans=0x3f3f3f3f; this->datas=packageSum; dfs(0,0,0,0);//从第0个物品开始考虑 return ans; } };
时间复杂度: ,每个物品有两种方案,总共有n个物品,故方案总数为 ,时间复杂度为
空间复杂度: ,存储物品数据所需 级别的内存,递归的深度也为 级别,故空间复杂度为
方法二(二维费用背包,可AC)
显然这是二维费用背包问题,对于每个物品,第一维费用为 ,第二维费用为 ,价值为 。
具体的,我们可以设 表示前 个物品,当前第一维 刚好花费 的费用为 ,第二维 刚好花费 的费用为 ,价值总和的最小值。
显然有:
由于每个 和 都是正数,我们可以采用 滚动数组 的形式优化掉第一维空间,
即:
这边还有一个问题,就是我们最后所选择的费用总和可以大于当前已有的费用总和,即最后所选物品的 总和可以大于 ,或者所选物品的 总和可以大于 。
那么我们可以如何处理呢?
我们设当前背包的容量分别为『以前的j』,『以前的k』,另外有新的状态表示的背包容量『新的j』,『新的k』
我们以『以前的j』,『以前的k』作考量:
假如对于『以前的j』,『以前的k』,有一个物品的费用超出了其大小
那么我们可以将当前物品『压缩大小』,放入背包中
代码:
class Solution { public: int f[2001][2001]; int minCost(int breadNum, int beverageNum, vector<vector<int> >& packageSum) { memset(f,0x3f,sizeof f);//初始化一个很大的值 f[0][0]=0;//边界 for(int i=0;i<packageSum.size();i++){ for(int j=breadNum;j>=0;j--){ for(int k=beverageNum;k>=0;k--){ //max(j-packageSum[i][0],0)和 max(k-packageSum[i][1],0) 即把当前物品的费用压缩到j和k f[j][k]=min(f[j][k],f[max(j-packageSum[i][0],0)][max(k-packageSum[i][1],0)]+packageSum[i][2]); } } } return f[breadNum][beverageNum]; } };
时间复杂度: ,动态规划转移的过程中有3重循环,第一重是 的,第二重是 的,第三重是 的,故总的时间复杂度为
空间复杂度: ,我们采用滚动数组将 优化到 ,这两维都是表示费用大小,故总的空间复杂度为