分享

0-1背包问题

 昵称3436139 2010-11-14

0-1背包问题

(2009-11-04 11:19:52)

01背包问题

题目描述: ( 转自背包九讲1 )

有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]。

我的理解:

状态转移方程很好理解,我们想你的包是固定大小的,而一堆东西在外面,我们可以选择放当前拿到手的物品,那包的体积肯定要减小,如果我们不放这个物品,那背包里的物品价值和容量当然保持不变了.

我喜欢设状态数组为DP[i][v];

那么下标分别是什么意思呢? 我们可以将 i 当成物品的编号,把v当成当前背包的体积

PKU ACM 有道原题链接:http://acm.pku.edu.cn/JudgeOnline/problem?id=3624

题目大意:

给定一个背包的物品数量和背包体积,然后分别输入每个物品的体积和价值
最后输出背包装满后最大价值量

样例输入:
4 6
1 4
2 6
3 12
2 7
样例输出:
23

我们输出DP数组看一下:
  6
1 4  4
2 4  10 10 10 10
3 4  12 16 18 22
4 4  12 16 19 23

最上一排和最左一排是行下标和列下标,我们看到 DP[1][1] 代表什么呢? 代表有第1个物品,并且当前背包体积为1的时候最大的量,当然是4了,而DP[2][2]呢? 代表当前放入物品2,因为当前体积为2,而物品2体积就是2了,所以1就不能放了,好,DP[2][2] = 6,同理,DP[4][6] 就代表体积装满的时候,背包里面的总价值,为23.

这里时间复杂度不能优化了,而我们可以优化空间复杂度,我们观察DP数组的最后一行:
先考虑上面讲的基本思路如何实现,肯定是有一个主循环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背包问题是十分必要的。
核心程序:
for ( int i = 1; i <= N; i++ )
{
 for ( int v = V; v>=1; --v )
 {
  if ( v >= cost[i] )
  {
   if ( DP[v-cost[i]] + value[i] >= DP[v] ) {
                         DP[v] = DP[v-cost[i]] + value[i];      
              }
}
 //DP[v-cost[i]]+value[i]就是说目前包里要装第i个物品
//看看装第i个物品和不装它哪个大,第一次开始时DP[6],就是说可以装
//单位重量的物品,然后依次类推,5,4,3,2,1,看看有或没有第i个物品的总价值

C++ 代码:


#include <iostream>
#include <vector>
using namespace std;

int main ()
{
 vector<int> value;
 vector<int> volume;
 vector<int> DP;
 int N,V;
 while ( cin >> N >> V )
 {
  value.resize(N+1);
  volume.resize(V+1);
  DP.clear();
  DP.resize(V+1);
  for ( int i = 1; i <= N; i++ )
  {
   cin >> volume[i] >> value[i];
  }
  for ( int i = 1; i <= N; i++ )
  {
   for ( int v = V; v >= 1; v-- )
   {
    if ( v >= volume[i] )
    {
     if ( DP[v-volume[i]] + value[i] >= DP[v] )
     {
      DP[v] = DP[v-volume[i]] + value[i];
     }
    }
   }
  }
  cout << DP[V] << endl;
 }
 return 0;
}

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多