池塘的水满了,雨也停了,田边的稀泥里到处是泥鳅。
不过,今天不捉泥鳅,而是去捉螃蟹,待会儿太阳小点就出发。拿个袋子,当然是捉得越多越好,于是想到类似的经典问题:
小偷夜入宝地,可偷宝贝甚多,然背包承重有限,最大收益几何?
这是一个典型的0/1背包问题,为什么叫0/1背包问题呢?因为对于一件宝贝,小偷要么选择偷它,要么选择不偷它。小偷陷入了左右为难的境地,我们对小偷的困难进行分析和具体化。
假设小偷进入宝地,发现了4件宝贝,其价值V分别是2元、3元、4元、5元,对应的重量W分别是1斤、2斤、3斤、4斤,小偷的背包最大承重是7斤,那么小偷的最大收益是多少元呢?
i | 1 | 2 | 3 | 4 |
V | 2元 | 3元 | 4元 | 5元 |
W | 1斤 | 2斤 | 3斤 | 4斤 |
显然,可用动态规划来思考,并找出递推关系式。我们来定义状态:在承重为j的限制下,记f[i][j]是前i件宝贝能达到的最大收益。那么,F[4][7]即为小偷的目标。现在的问题是:要求出f[i][j]的递推关系式。
我们考察第i件宝贝,只有两种情况:小偷要么选择它,要么不选择它。然后在这两种情况中求较大值,便形成了递推关系式。直接看程序,递推关系式便体现在其中:
#include<iostream>
using namespace std;
#define N 4 // 总共N件宝贝
#define V 7 // V是背包的承重极限值
int main()
{
int value[N + 1] = {0, 2, 3, 4, 5}; // 宝贝的价值
int weight[N + 1] = {0, 1, 2, 3, 4}; // 宝贝的重量
int f[N + 1][V + 1] = {0}; // f[i][j]表示在背包承重为j的情况下,前i件宝贝的最大价值
int i = 1;
int j = 1;
for(i = 1; i <= N; i++)
{
for(j = 1; j <= V; j++)
{
// 递推关系式
if(j < weight[i])
{
f[i][j] = f[i - 1][j];
}
else
{
int x = f[i - 1][j]; // case1: 不选择第i件宝贝
int y = f[i - 1][j - weight[i]] + value[i]; // case2: 选择第i件宝贝
f[i][j] = x < y ? y : x;
}
}
}
cout << f[N][V] << endl; // 10
return 0;
}
程序的结果是10元, 这就是小偷的最大收益。
在之前的文章中,我们聊过动态规划:动态规划的本质。熟练掌握动态规划,才能获得最大收益,能捉到的螃蟹也会更多。
走,捉螃蟹去。