题意:N块石头,每块石头有各自的价值与体积,将石头放入体积为V的背包中,能获得的最大价值是多少。
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2602
——>>0-1背包问题,设d[i][j]为将 前i块石头 放入 容量为j的背包 中的 最大价值,那么第i块石头,要么放,要么不放,放的话,d[i][j] = d[i-1][j-vol[i]],不放的话,d[i][j] = d[i-1][j],取两者中的最大者。
#include <iostream>
#include <algorithm>using namespace std;const int maxn = 1000 + 10; //N <= 1000 , V <= 1000
int d[maxn][maxn]; //d[i][j]表示将 前i块石头 放入 容量为j的背包 中的 最大价值int main()
{int T, N, V, i, j, val[maxn], vol[maxn];cin>>T;while(T--){cin>>N>>V;for(i = 1; i <= N; i++) //输入价值cin>>val[i];for(i = 1; i <= N; i++) //输入体积cin>>vol[i];for(i = 0; i <= V; i++) d[0][V] = 0; //任何一个背包,不入石头,则得到的价值为0for(i = 1; i <= N; i++) //共N块石头for(j = 0; j <= V; j++) //容量从0到V,因为是整数,恰好可作数组下标{if(j >= vol[i]) //如果背包放得下这块石头d[i][j] = max(d[i-1][j], d[i-1][j-vol[i]]+val[i]); //取放与不放这块石头较大者else d[i][j] = d[i-1][j]; //肯定取不了这块石头}cout<<d[N][V]<<endl; //将前N块石头放在容量为N的背包}return 0;
}
今天学了个滚动数组,再做一次,换了种方法:
——>>滚动数组感悟:
例:0-1背包问题:
n件物品:
物品体积(V):1 2 3
物品重量(W):3 2 1
背包容量(Q):4
记f[i][j]表示在前i件物品中选择物品放到容量(体积)不超过j的背包中的最大重量。
则状态转移方程为:f[i][j] = max(f[i-1][j], f[i-1][j-V] +W);
看这个样子
int d[n+10];全部初始为0!!!
for(i = 1; i <= n; i++)
for(j = Q; j >= 0; j--)
d[j] = max(d[j], d[j-V] + W);
请观:
处理第1个物品时:
d[4] = max(d[4], d[4-1] + 3] = max(d[4], d[3] + 3) = max(0, 0 + 3) = 3;
d[3] = max(d[3], d[3-1] + 3] = max(d[3], d[2] + 3) = max(0, 0 + 3) = 3;
d[2] = max(d[2], d[2-1] + 3] = max(d[2], d[1] + 3) = max(0, 0 + 3) = 3;
d[1] = max(d[1], d[1-1] + 3] = max(d[1], d[0] + 3) = max(0, 0 + 3) = 3;
d[0] = 0;
---------------------------------------------------------------------------------------------
处理第2个物品时:
d[4] = max(d[4], d[4-2] + 2] = max(d[4], d[2] + 2) = max(3, 3 + 2) = 5; //d[4]记算前,实际上是f[i-1][4],d[4-2]+2是f[i-1][4-2],都是上一层的结果;
d[3] = max(d[3], d[3-2] + 2] = max(d[3], d[1] + 2) = max(3, 3 + 2) = 5; //d[3]记算前,实际上是f[i-1][3],d[3-2]+2是f[i-1][3-2],都是上一层的结果;
d[2] = max(d[2], d[2-2] + 2] = max(d[2], d[0] + 2) = max(3, 0 + 2) = 3; //d[2]记算前,实际上是f[i-1][2],d[2-2]+2是f[i-1][2-2],都是上一层的结果;
d[1] = 3; //d[1]记算前,实际上是f[i-1][1],都是上一层的结果;
d[0] = 0; //d[0]记算前,实际上是f[i-1][0],都是上一层的结果;
---------------------------------------------------------------------------------------------
处理第3个物品时:
d[4] = max(d[4], d[4-3] + 1] = max(d[4], d[1] + 1) = max(5, 3 + 1) = 5; //同上d[3] = max(d[3], d[3-3] + 1] = max(d[3], d[0] + 1) = max(5, 0 + 1) = 5; //同上
d[2] = 3; //同上
d[1] = 3; //同上
d[0] = 0; //同上
可以看出,这样做用新的d[j]利用上一层d[j]的值经处理覆盖了原来的值,不再将上一层的值保存在一个数组中,达到了同样的效果,
开始我在疑惑,为什么要for(j = Q;, j >=0; j--),为什么不从小到大,写下,通也!
若是从小到大:
d[0] = 0;
d[1] = max(d[1], d[1-1] + 3] = max(d[1], d[0] + 3) = max(0, 0 + 3) = 3;
d[2] = max(d[2], d[2-1] + 3] = max(d[2], d[1] + 3) = max(0, 3 + 3) = 6;
......可以看到,这样做d[i]更新时,利用的不是上一层的结果,而是现在这一层的结果,当然错误啦……
#include <cstdio>
#include <algorithm>
#include <cstring>using namespace std;const int maxn = 1000 + 10;int main()
{int T, N, V, val[maxn], vol[maxn], d[maxn], i, j;scanf("%d", &T);while(T--){scanf("%d%d", &N, &V);for(i = 1; i <= N; i++) scanf("%d", &val[i]);for(i = 1; i <= N; i++) scanf("%d", &vol[i]);memset(d, 0, sizeof(d));for(i = 1; i <= N; i++)for(j = V; j >= 0; j--)if(j - vol[i] >= 0) d[j] = max(d[j], d[j-vol[i]] + val[i]);printf("%d\n", d[V]);}return 0;
}
小心初始化d数组和判断条件!!!
一时兴起,再来个Java的,只是在初始化d 的时候,WA了两次,第一次,没从0开始,第二次,终点设V成了N……
import java.util.Scanner;
public class Main {static final int maxn = 1000 + 10;public static void main(String[] args) {int T, N, V, val[] = new int[maxn], vol[] = new int[maxn], d[] = new int[maxn];Scanner cin = new Scanner(System.in);T = cin.nextInt();while(T-->0){N = cin.nextInt();V = cin.nextInt();for(int i = 1; i <= N; i++) val[i] = cin.nextInt();for(int i = 1; i <= N; i++) vol[i] = cin.nextInt();for(int i = 0; i <= V; i++) d[i] = 0;for(int i = 1; i <= N; i++)for(int j = V; j >= 0; j--)if(j - vol[i] >= 0) d[j] = d[j] < d[j-vol[i]]+val[i] ? d[j-vol[i]]+val[i] : d[j];System.out.println(d[V]);}cin.close();}
}