• HDU 2546 01背包问题


    http://acm.hdu.edu.cn/showproblem.php?pid=2546

    背包问题是一个经典的动态规划模型。它既简单形象容易理解,又在某种程度上能够揭示动态规划的本质,故不少教材都把它作为动态规划部分的第一道例题。

    01背包问题,可以这么理解。

    题目

    有N件物品和一个容量为V的背包。第i件物品的费用是c[i],价值是w[i]。求解将哪些物品装入背包可使价值总和最大。


    基本思路

    这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。

    用子问题定义状态:即f[i][v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。则其状态转移方程便是:


    f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]+w[i]}

    这 个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生出来的。所以有必要将它详细解释一下:“将前i件物品放入容量为v的背包中”这个子问题, 若只考虑第i件物品的策略(放或不放),那么就可以转化为一个只牵扯前i-1件物品的问题。如果不放第i件物品,那么问题就转化为“前i-1件物 品放入容量为v的背包中”,价值为f[i-1][v];如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为v-c[i]的背包中”,此 时能获得的最大价值就是f[i-1][v-c[i]]再加上通过放入第i件物品获得的价值w[i]。


    优化空间复杂度

    以上方法的时间和空间复杂度均为O(VN),其中时间复杂度应该已经不能再优化了,但空间复杂度却可以优化到O。

    先 考虑上面讲的基本思路如何实现,肯定是有一个主循环i=1..N,每次算出来二维数组f[i][0..V]的所有值。那么,如果只用一个数组 f[0..V],能不能保证第i次循环结束后f[v]中表示的就是我们定义的状态f[i][v]呢?f[i][v]是由f[i-1][v]和f[i-1] [v-c[i]]两个子问题递推而来,能否保证在推f[i][v]时(也即在第i次主循环中推f[v]时)能够得到f[i-1][v]和f[i-1] [v-c[i]]的值呢?事实上,这要求在每次主循环中我们以v=V..0的顺序推f[v],这样才能保证推f[v]时f[v-c[i]]保存的是状态 f[i-1][v-c[i]]的值。伪代码如下:

    for i=1..N
        for v=V..0
            f[v]=max{f[v],f[v-c[i]]+w[i]};

    其中的f[v]=max{f[v],f[v-c[i]]}一句恰就相当于我们的转移方程
    f[i][v]=max{f[i-1][v],f[i-1][v-c[i]]}
    ,因为现在的f[v-c[i]]就相当于原来的f[i-1][v-c[i]]。如果将v的循环顺序从上面的逆序改成顺序的话,那么则成了f[i][v]由f[i][v-c[i]]推知,与本题意不符,但它却是另一个重要的背包问题P02最简捷的解决方案,故学习只用一维数组解01背包问题是十分必要的。

    事实上,使用一维数组解01背包的程序在后面会被多次用到,所以这里抽象出一个处理一件01背包中的物品过程,以后的代码中直接调用不加说明

     

     1 #include<stdio.h>
     2 #include<stdlib.h>
     3 #include<string.h>
     4 int main()
     5 {
     6     int i, j;
     7     int m, n, max, v, maxz;
     8     int w[1001], p[1001];
     9     while(1)
    10     {
    11         memset(p, 0, sizeof(p));
    12         scanf("%d", &n);
    13         if(n == 0)
    14         break;
    15         maxz = 0;
    16         for(i = 0; i < n; i++)
    17         {
    18             scanf("%d", &w[i]);
    19             if(w[i] > w[maxz])
    20                 maxz = i;
    21         }
    22         scanf("%d", &m);
    23         if(m < 5)
    24         {
    25             printf("%d\n",m);
    26             continue;
    27         }
    28         v = m - 5;
    29         for(i = 0; i < n; i++)
    30         {
    31               if(i != maxz)
    32               for(j = v; j >= w[i]; j--)
    33               {
    34                     if(p[j - w[i]] + w[i] > p[j] )
    35                      p[j] = p[j - w[i]] + w[i];
    36               }
    37         }
    38         printf("%d\n",m - p[v] - w[maxz]);
    39     }
    40     return 0;
    41 }   

     

     



  • 相关阅读:
    iOS崩溃报告获取一
    GCDTimer
    Runtime
    Socket
    冒泡排序笔记
    学习java虚拟机笔记
    ftp发送文件包括中文名
    java email
    批量数据插入高效 转发
    读取本地硬盘文件,快速扫描插入 数据库
  • 原文地址:https://www.cnblogs.com/zxj015/p/2740204.html
Copyright © 2020-2023  润新知