由DAG到背包问题——记忆化搜索和递推两种解法

2018/08/08 23:12
阅读数 71

一、问题描述

物品无限的背包问题:有n种物品,每种均有无穷多个。第 i 种物品的体积为Vi,重量为Wi。选一些物品装到一个容量为 C 的背包中,求使得背包内物品总体积不超过C的前提下重量的最大值。1≤n≤100, 1≤Vi≤C≤10000, 1≤Wi≤1000000.

二、解题思路

我们可以先求体积恰好为 i 时的最大重量(设为d[i]),然后取d[i]中的最大值(i ≤ C)。与之前硬币问题,“面值恰好为S”就类似了。只不过加了新属性——重量,相当于把原来的无权图改成带权图,即把“+1”变成“+W[j]”。这样,问题就变成了求以C为起点、终点任意的,边权之和最大的路径。

三、代码实现

1、记忆化搜索

之前纠结这种方法的时间复杂度,先给结果:O(maxn * maxc)。因为计算dp(s)时,如果dp[i]中i是从0-->C,

则dp[i] = max(dp[i],dp[i - V[j]] + W[j]),dp[i - V[j]]已经计算出来且保存,相当于得到dp[i]没有花费时间。如果dp[i]中i是从C-->0,

每次计算的都被保存且只计算一次,有几次小的递归,也相当于没有花费时间。

 

 1 #include<stdio.h>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 
 7 const int INF = 0x3f3f3f3f;
 8 const int maxn = 100 + 10;
 9 const int maxc = 10000 + 10;
10 int n,V[maxn],W[maxn],C;
11 int d[maxc];        //d[i]表示总体积恰好为i时的最大重量
12 
13 int dp(int s)
14 {
15     int& ans = d[s];
16     if (ans != -1)  return ans;
17     ans = - INF;
18     for (int i = 0; i < n; i++)
19     {
20         if (s >= V[i])  ans = max(ans, dp(s - V[i]) + W[i]);
21     }
22     return ans;
23 }
24 void slove()
25 {
26     memset(d, -1, sizeof(d));
27     d[0] = 0;
28     int res = -1;
29     for (int i = 0; i <= C; i++)
30         res = max(res, dp(i));
31     printf("%d\n", res);
32 }
33 
34 int main()
35 {
36     while (scanf("%d",&n) == 1 && n)
37     {
38         scanf("%d", &C);
39         for (int i = 0; i < n; i++)
40             scanf("%d%d", &V[i], &W[i]);
41 
42         slove();
43     }
44     return 0;
45 }

 

2、递推式

这种写法时间复杂度十分显然,与记忆化搜索相同,都是O(maxn * maxc)。但必须注意循环的顺序,比如容量只能从0-->C,而不能反过来,前一种写法则没有循环的顺序要求。

 1 void slove()
 2 {
 3     fill(d, d + n, -INF);
 4     d[0] = 0;
 5     int res = -1;
 6     for (int i = 0; i <= C; i++)        //容量的循环顺序只能是从小到大
 7     {
 8         for (int j = 0; j < n; j++)
 9         {
10             if(i >= V[j])  d[i] = max(d[i], d[i - V[j]] + W[j]);
11         }
12         res = max(res, d[i]);
13     }
14     printf("%d\n", res);
15 }

3、两者比较

在得到状态转移方程之后,还需要思考如何编写程序。尽管在很多情况下,记忆化搜索程序更直观、易懂,但在0-1背包中递推法更理想。因为已知状态转移方程后,递推法的难点是循环顺序,而有了“阶段”定义后,循环顺序变得十分显然。

 

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部