背包问题
一 背包问题的一般形式
例题
例:张三去抢劫时,带了一个容积为8的背包,等他闯进店里,发现店里有四件物品,如何装他的背包可使获利最大。
items | space | value |
---|---|---|
电脑(1) | 3 | 1500 |
吉他(2) | 5 | 1000 |
平板(3) | 2 | 500 |
音响(4) | 1 | 200 |
分析:
张三在拿取某物品时,会出现两种状态:
1.空间不足,这个物品无法拿取
2.空间足够,可以拿取也可以不拿取(拿取不一定是最优解,因为你只拿三个物品不一定就比拿四个物品便宜)
状态一:
bag[i][j]=bag[i-1][j];
// i指的是第i个物品,j指的是背包的容量,bag[i][j]指的是在j空间下,对前i个物品进行选择(不一定拿)得到的最大价值
状态二:
bag[i][j]=max(bag[i-1][j],bag[i-1][j-space[i]]+value[i])
// 比较在j空间下选择到i-1个物品和在j空间下选择了第i个物品的价值,判断方案中是否包应该含有物品i
根据这两个状态转移方程我们可以得到下面的表格
i\j | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
1 | 0 | 0 | 1500 | 1500 | 1500 | 1500 | 1500 | 1500 |
2 | 0 | 0 | 0 | 1500 | 1500 | 1500 | 1500 | 1500 |
3 | 0 | 500 | 1500 | 1500 | 2000 | 2000 | 2000 | 2500 |
4 | 200 | 500 | 1500 | 1700 | 2000 | 2200 | 2500 | 2500 |
发现最大值为2500,用代码表示表格如下
for (int i=1; i<=m; i++) {
for (int j=1; j<=t; j++) {
if(j<tim[i]) bag[i][j]=bag[i-1][j];
else bag[i][j]=max(bag[i-1][j],bag[i-1][j-tim[i]]+value[i]);
}
}
完整代码如下:
#include <stdio.h>
int value[200];
int space[200];
int bag[1010][1010];
int max(int x,int y)
{
if(x>y) return x;
return y;
}
int main() {
int t,m;
scanf("%d%d",&t,&m);
for (int i=1; i<=m; i++) {
scanf("%d%d",&space[i],&value[i]);
}
for (int i=1; i<=m; i++) {
for (int j=1; j<=t; j++) {
if(j<space[i]) bag[i][j]=bag[i-1][j];
else bag[i][j]=max(bag[i-1][j],bag[i-1][j-space[i]]+value[i]);
}
}
printf("\n%d",bag[m][t]);
return 0;
}
遍历每一种投资可能,从bag[1][1]到最终的bag[4][8],在每一种空间下,判断是否包含某个物品,最终累加得到最终的最优解。
二.回溯解的组成
毕竟还得知道到底怎么抢…
此时只需要倒退回去,判断是否拿取
bag[i-1][j]<bag[i-1][j-space[i]]+value[i]
// 如果前者小于后者,则说明是拿取了第i个物品
从最上面的物品,总空间开始,向下递推,打印解
int tcopy =t
for (int i=m; i>0; i--) {
if(bag[i-1][tcopy]<bag[i-1][tcopy-space[i]]+value[i]){ printf("%d ",i);tcopy-=space[i];}
}
大家如果有不清楚的地方欢迎在评论区留言,我会为大家解答😘