动态规划之0/1背包有关问题

动态规划之0/1背包问题

背包问题

它是在1978年由Merkel和Hellman提出的。它的主要思路是假定某人拥有大量物品,重量各不同。此人通过秘密地选择一部分物品并将它们放 到背包中来加密消息。背包中的物品中重量是公开的,所有可能的物品也是公开的,但背包中的物品是保密的。附加一定的限制条件,给出重量,而要列出可能的物 品,在计算上是不可实现的。背包问题是熟知的不可计算问题,背包*以其加密,解密速度快而其人注目。但是,大多数一次背包*均被破译了,因此现在很少有人使用它。
0/1背包问题
  有N件物品和一个容量为V的背包。第i件物品的费用是c,价值是w。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
  基本思路
  这是最基础的背包问题,特点是:每种物品仅有一件,可以选择放或不放。
  用子问题定义状态:即f[v]表示前i件物品恰放入一个容量为v的背包可以获得的最大价值。则其状态转移方程便是:f[v]=max{f[v],f[v-c]+w}。
  这个方程非常重要,基本上所有跟背包相关的问题的方程都是由它衍生出来的。所以有必要将它详细 解释一下:“将前i件物品放入容量为v的背包中”这个子问题,若只考虑第i件物品的策略(放或不放),那么就可以转化为一个只牵扯前i-1件物品的问题。 如果不放第i件物品,那么问题就转化为“前i-1件物品放入容量为v的背包中”;如果放第i件物品,那么问题就转化为“前i-1件物品放入剩下的容量为 v-c的背包中”,此时能获得的最大价值就是f [v-c]再加上通过放入第i件物品获得的价值w。
  注意f[v]有意义当且仅当存在一个前i件物品的子集,其费用总和为v。所以按照这个方程递推 完毕后,最终的答案并不一定是f[N] [V],而是f[N][0..V]的最大值。如果将状态的定义中的“恰”字去掉,在转移方程中就要再加入一项f[v-1],这样就可以保证f[N] [V]就是最后的答案。至于为什么这样就可以,由你自己来体会了。
  优化空间复杂度
  以上方法的时间和空间复杂度均为O(N*V),其中时间复杂度基本已经不能再优化了,但空间复杂度却可以优化到O(V)。

下面看一道最基础的o/1背包问题:

poj 3624:

题意:给你n件物品以及这n件物品的质量与价值,另外给你一个背包以及背包自身的最大容量,求在满足背包最大容量的条件下的,所装物品的最大价值。

思路参见0/1背包问题。

代码参考如下:

#include<iostream>
using namespace std;
const int mMax = 3500;
const int nMax = 14000;

struct
{
	int wei, val;
}obj[mMax];

int main()
{
    int n, m, i, w, dp[nMax];
    cin >> n >> m;
    for(i = 1; i <= n; i ++)
        cin >> obj[i].wei >> obj[i].val;     //  改为scanf()会省跑100多MS。
    memset(dp, 0, sizeof(dp));
	
    for(i = 1; i <= n; i ++)
        for(w = m; w >= obj[i].wei; w --)    //  必须从后往前dp,因为前面的数会影响后面的。
            if(dp[w] < dp[w - obj[i].wei] + obj[i].val)
                dp[w] = dp[w - obj[i].wei] + obj[i].val;
			cout << dp[m] << endl;
			return 0;
}

 关于0/1背包问题,接下来自己还会深入学习。