二维费用的背包
•对于每种物品,具有两种不同的费用,即选择这种物品必须同时付出这两种代价;对于每种代价都有一个可付出的最大值(背包容量)。问怎样选择可以得到最大的价值。
•设这两种代价分别为代价1和代价2,第i件物品所需的两种代价分别为a[i]和 b[i]。两种代价可付出的最大值(两种背包容量)分别为V和U。物品的价值为w[i]。
•算法:
• 费用加了一维,只需状态也加一维即可。设f[i][v][u]表示前i件物品付出两种代价分别为v和u时可获得的最大价值。状态转移方程就是:f[i][v][u]=max{f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+w[i]}。
•01背包:f[i][v]=max{f[i-1][v],f[i-1][v-a[i]]+c[i]}
优化后代码对比:
•f[i][v]=max{f[i-1][v],f[i-1][v-a[i]]+c[i]}
•for(int i=1;i<=n;i++)
• for(int j=v;j>=a[i];j--)
• f[j]=max(f[j],f[j-a[i]]+c[i]);
•f[i][v][u]=max{f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+c[i]}
•for(int i=1;i<=n;i++)
• for(int j=v;j>=a[i];j--)
• for(int k=u;k>=b[i];k--)
• f[j][k]=max(f[j][k],f[j-a[i]][k-b[i]]+c[i]);
题目:潜水员
【题目描述】
潜水员为了潜水要使用特殊的装备。他有一个带2种气体的气缸:一个为氧气,一个为氮气。让潜水员下潜的深度需要各种的数量的氧和氮。潜水员有一定数量的气缸。每个气缸都有重量和气体容量。潜水员为了完成他的工作需要特定数量的氧和氮。他完成工作所需气缸的总重的最低限度的是多少?
例如:潜水员有5个气缸。每行三个数字为:氧,氮的(升)量和气缸的重量:
如果潜水员需要5升的氧和60升的氮则总重最小为249(1,2或者4,5号气缸)。
你的任务就是计算潜水员为了完成他的工作需要的气缸的重量的最低值。
【输入】
第一行有2整数m,n(1≤m≤21,1≤n≤79)。它们表示氧,氮各自需要的量。
第二行为整数k(1≤n≤1000)表示气缸的个数。
此后的k行,每行包括ai,bi,ci(1≤ai≤21,1≤bi≤79,1≤ci≤800)3ai,bi,ci(1≤ai≤21,1≤bi≤79,1≤ci≤800)3整数。这些各自是:第i个气缸里的氧和氮的容量及汽缸重量。
【输出】
仅一行包含一个整数,为潜水员完成工作所需的气缸的重量总和的最低值。
【输入样例】
5 60
5
3 36 120
10 25 129
5 50 250
1 45 130
4 20 119
【输出样例】
249
01背包分析:
背包容量:不超过载重量m或体积v
物品的代价:每种物品的重量或体积
物品的价值:每个物品的价值
求:放入不超过背包容量的物品的最大价值和
本题分析(注意与01背包的不同点):
背包容量:至少5升氧 60升氮
物品的代价:每个气缸的氧氮数
物品的价值:气缸的重量
求:达到背包要求的物品重量的最小值
分析:简化为只需要氮
输入更改为:
【输入样例】
60
5
36 120
25 129
50 250
45 130
20 119
【输出样例】
249
f[i][d] | 0 | 10 | 15 | 20 | 25 | 30 | 36 | 40 | 45 | 50 | 55 | 60 | |
0 | 0 | 0 | ∞ | ∞ | ∞ | ∞ | ∞ | ∞ | ∞ | ∞ | ∞ | ∞ | ∞ |
1 | 36 | 0 | 120 | 120 | 120 | 120 | 120 | 120 | ∞ | ∞ | ∞ | ∞ | ∞ |
2 | 25 | 0 | 120 | 120 | 120 | 120 | 120 | 120 | 249 | 249 | 249 | 249 | 249 |
3 | 50 | 0 | 120 | 120 | 120 | 120 | 120 | 120 | 249 | 249 | 249 | 249 | 249 |
4 | 45 | 0 | 120 | 120 | 120 | 120 | 120 | 120 | 130 | 130 | 249 | 249 | 249 |
5 | 20 | 0 | 119 | 119 | 119 | 120 | 120 | 120 | 130 | 130 | 239 | 239 | 249 |
分析并写出代码:
for(int i=1;i<=k;i++)
for(int d=0;d<=n;d++)
if(d>=b[i])
f[i][d]=min(f[i-1][d],f[i-1][d-b[i]]+c[i];
else
f[i][d]=min(f[i-1][d],f[i-1][0]+c[i]);
优化1-代码简洁:
for(int i=1;i<=k;i++)
for(int d=0;d<=n;d++)
{
int t=d-b[i];
if(t<0) t=0;
f[i][d]=min(f[i-1][d],f[i-1][t]+c[i]);
}
优化2--滚动数组:
for(int i=1;i<=k;i++)
for(int d=n;d>=0;d++)
{
int t=d-b[i];
if(t<0) t=0;
f[d]=min(f[d],f[t]+c[i]);
}
类推到本题(一维变二维):
f[i][v][u]=max{f[i-1][v][u],f[i-1][v-a[i]][u-b[i]]+c[i]}
关键代码:
for(int i=1;i<=k;i++)
for(int j=m;j>=0;j--
for(int d=n;d>=0;d--)
{
int t1=j-a[i];
int t2=d-b[i];
if(t1<0) t1=0;
if(t2<0) t2=0;
f[j][d]=min(f[j][d],f[t1][t2]+c[i]);
}
全部代码:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int m,n,k;
int a[1005],b[1005],c[1005];
int f[101][101];
int main()
{
scanf("%d%d",&m,&n);
scanf("%d",&k);
for(int i=1;i<=k;i++)
scanf("%d%d%d",&a[i],&b[i],&c[i]);
memset(f,0x3f,sizeof(f));//求最小值
f[0][0]=0;
for(int i=1;i<=k;i++)
for(int j=m;j>=0;j--)//枚举氧含量
for(int d=n;d>=0;d--)//枚举氮含量
{
int t1=j-a[i];
int t2=d-b[i];
if(t1<0) t1=0;
if(t2<0) t2=0;
f[j][d]=min(f[j][d],f[t1][t2]+c[i]);
}
printf("%d",f[m][n]);
return 0;
}
这种做法是本人思考总结出来的,希望转载时能备注出处,非常感谢。