您的位置:首页 > 其它

01背包问题

2017-03-17 12:51 253 查看

一:问题

  有N件物品和一个容量为V的背包。第i件物品的体积是Ci,其价值是Wi。求解,在不超过背包容量情况下,将哪些物品装入背包可使价值总和最大。

二:基本思路

  这是最基础的背包问题,特点是:每种物品仅有一件。

  状态 F[i,v]表示前i件物品中选择若干件放在容量为v的背包中,可以取得的最大价值。

  转移方程

F[i,v]=max{F[i−1,v],F[i−1,v−Ci]+Wi}

对于第i件物品,有放与不放两种选择。若选择不放,F[i,v]=F[i−1,v];若选择放,v−Ci确保有足够的空间,随之F[i,v]=F[i−1,v−Ci]+Wi。

三:代码

/**
*
* author 刘毅(Limer)
* date   2017-03-17
* mode   C++
*/
#include<iostream>
#include<algorithm>
using namespace std;

int main()
{
const int N = 6;                     //物品个数
const int V = 10;                    //背包体积
int C[N + 1] = { -1,5,6,5,1,19,7 };  //第i个物品的体积(下标从1开始)
int W[N + 1] = { -1,2,3,1,4,6,5 };   //第i个物品的价值
int F[N + 1][V + 1] = { 0 };         //状态

for (int i = 1; i <= N; i++)  //对于第i个物品
for (int v = 0; v <= V; v++)
{
F[i][v] = F[i - 1][v];  //第i个不放
if (v - C[i] >= 0 && F[i][v] < F[i - 1][v - C[i]] + W[i])  //如果比它大,再放第i个
F[i][v] = F[i - 1][v - C[i]] + W[i];
}

cout << "最大价值是:" << F
[V] << endl;  //9

return 0;
}


四:空间复杂度优化

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

  先考虑上面讲的基本思路如何实现,肯定是有一个主循环
i ← 1 to N
,每次算出来二维数组F[i,v]的所有值。那么,如果只用一个数组F[v]能不能保证第i次循环结束后F[v]中表示的就是我们定义的状态F[i,v]呢?

  F[i,v]是由F[i−1,v]和F[i−1,v−Ci]两个子问题递推而来,能否保证在推F[i,v]时(也即在第i次主循环中推F[v]时)能够取用F[i−1,v]和F[i−1,v−Ci]的值呢?

  事实上,这要求在每次主循环中我们以
v ← V to C[i]
的递减顺序计算F[v],这样才能保证计算F[v]时F[v−Ci]保存的是状态F[i−1,v−Ci]的值。

  优化后的代码如下:

/**
*
* author 刘毅(Limer)
* data   2017-03-17
* mode   C++
*/
#include<iostream>
#include<algorithm>
using namespace std;

int main()
{
const int N = 6;                     //物品个数
const int V = 10;                    //背包体积
int C[N + 1] = { -1,5,6,5,1,19,7 };  //第i个物品的体积(下标从1开始)
int W[N + 1] = { -1,2,3,1,4,6,5 };   //第i个物品的价值
int F[V + 1] = { 0 };                //状态

for (int i = 1; i <= N; i++)  //对于第i个物品
for (int v = V; v >= C[i]; v--)
F[v] = max(F[v], F[v - C[i]] + W[i]);

cout << "最大价值是:" << F[V] << endl;  //9

return 0;
}


五:初始化的细节问题

  我们看到的求最优解的背包问题题目中,事实上有两种不太相同的问法。有的题目要求“恰好装满背包”时的最优解,有的题目则并没有要求必须把背包装满。这两种问法的实现方法只是在初始化的时候有所不同。

  如果是第一种问法,要求恰好装满背包,那么在初始化时除了F[0]为0,其它F[1]…F[V]均设为−∞,这样就可以保证最终得到的F[V]是一种恰好装满背包的最优解。如果并没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该将F[0]…F[V]全部设为0。

  这是为什么呢?可以这样理解:初始化的F数组事实上就是在没有任何物品可以放入背包时的合法状态。如果要求背包恰好装满,那么此时只有容量为0的背包可以在什么也不装且价值为0的情况下被“恰好装满”,其它容量的背包均没有合法的解,属于未定义的状态,应该被赋值为-∞了。如果背包并非必须被装满,那么任何容量的背包都有一个合法解“什么都不装”,这个解的价值为0,所以初始时状态的值也就全部为0了。

参考文献:

[ 1 ] .背包九讲.

文章转自我的个人博客:http://www.61mon.com/index.php/archives/188/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: