当前位置: 代码迷 >> 综合 >> hdu - 2602 - Bone Collector
  详细解决方案

hdu - 2602 - Bone Collector

热度:24   发布时间:2024-01-10 13:57:45.0

题意: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();}
}