hdu 2602 Bone Collector 解题报告

   题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2602

   在没学01背包时做的,很遗憾的是,wa了很多次。

   wa代码 

 1 #include <iostream>
 2 #include <algorithm>
 3 using namespace std;
 4 
 5 struct back
 6 {
 7     int value;
 8     int volume;
 9 } backet[1005];
10 
11 int cmpvalue(back a, back b)
12 {
13     return a.value > b.value;
14 }
15 
16 int main()
17 {
18     int T, i, j, n, v, sumvalue, sumvolume, maxvalue;
19     while (cin >> T)
20     {
21         while (T--)
22         {
23             cin >> n >> v;
24             for (i = 0; i < n; i++)
25                 cin >> backet[i].value;
26             for (j = 0; j < n; j++)
27                 cin >> backet[j].volume;
28             sort(backet, backet+n, cmpvalue);  //按价值递减的顺序排列,保证下面的是从最大价值的物品开始试探的
29             maxvalue = sumvalue = sumvolume = 0;
30             for (i = 0; i < n; i++)
31             {
32                 sumvalue += backet[i].value;
33                 sumvolume += backet[i].volume;
34                 if (sumvalue > maxvalue && sumvolume <= v) //比当前最大价值大的话则更新maxvalue
35                 {    
36                     maxvalue = sumvalue;    
37                 }
38                 else        // 比当前最大价值小的话则不装载到背包里,即回退到上一个状态(未装载前)
39                 {
40                     sumvalue -= backet[i].value;
41                     sumvolume -= backet[i].volume;
42                 }
43                 
44             }
45             printf("%d\n", maxvalue);
46         }
47     }
48     return 0;
49 }

      在乌东兄的悉心指导下,终于知道如何错了......

      此为贪心法,但是不能保证得出来最终结果是最优的。例如这个例子:

      1
      3  4
      4  3  2
      3  2  2

       如果用贪心做的话,它第一个取的是最大的价值4,花费的容量为3,那么剩下的背包容量只有1,后面的物品不能够再装载,因为最少的花费2都比1大。而最大的价值应该是5,花费的容量恰好为给定的容量限制4      

       于是改用DP方法(自数塔(非常好理解)、最少拦截系统(看书看到的,但不太明白))来做,前面的介绍还是比较容易理解的,但是到了空间优化第二个循环为什么是反向的就很难理解了,通过笔算模拟,有点明白了。以下是摘自大神乌东兄的语录(应该不会怪我的,无请示就....):

     dp数组当前储存的是用前 0..i - 1 个物品构造的 0..V 容量的最优值。
     如果内循环系正向,假设计算 dp[j] 时选择了放入物品 i ,
    计算第 dp[ j + costi ] 时,也选择嘞放入物品 i, 这样物品 i 就用了多次。
    由于 dp[j] 的推导会与 dp[0 .. j - 1] 有关,如果内循环反向的话,可以保证
    dp[ 0.. j - 1 ] 不包含物品 i ,即保证物品只用鸟 一次。

     AC代码:

 1 #include <iostream>
 2 using namespace std;
 3 
 4 int main()
 5 {
 6     int i, j, T, N, V, c[1005], w[1005], dp[1005];
 7     while (cin >> T)
 8     {
 9         while (T--)
10         {
11             memset(dp, 0, sizeof(dp));
12             cin >> N >> V;
13             for (i = 0; i < N; i++)    
14                 cin >> w[i];
15             for (i = 0; i < N; i++)
16                 cin >> c[i];
17             for (i = 0; i < N; i++)
18             {
19                 for (j = V; j >= c[i]; j--)    //关键所在,如果正向,会使得一个物品可能被用多次
20 { 21 dp[j] = (dp[j] >= dp[j-c[i]] + w[i] ? dp[j]: dp[j-c[i]]+w[i]); 22 } 23 } 24 printf("%d\n", dp[V]); 25 } 26 } 27 return 0; 28 }

   

原文地址:https://www.cnblogs.com/windysai/p/3091514.html