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

一、问题描述

物品无限的背包问题:有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背包中递推法更理想。因为已知状态转移方程后,递推法的难点是循环顺序,而有了“阶段”定义后,循环顺序变得十分显然。

原文地址:https://www.cnblogs.com/lfri/p/9446353.html

时间: 2024-07-30 02:31:44

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

hdu4283 You Are the One 区间dp 记忆化搜索or递推

You Are the One Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 3032    Accepted Submission(s): 1352 Problem Description The TV shows such as You Are the One has been very popular. In order to

POJ3249 Test for Job 【DAG】+【记忆化搜索】

Test for Job Time Limit: 5000MS   Memory Limit: 65536K Total Submissions: 9201   Accepted: 2080 Description Mr.Dog was fired by his company. In order to support his family, he must find a new job as soon as possible. Nowadays, It's hard to have a job

编程算法 - 背包问题(记忆化搜索) 代码(C)

背包问题(记忆化搜索) 代码(C) 本文地址: http://blog.csdn.net/caroline_wendy 题目参考: http://blog.csdn.net/caroline_wendy/article/details/37912949 使用记忆化搜索, 需要存储每组的值, 下次不需要进行继续迭代, 可以降低至时间复杂度O(nW). 代码: /* * main.cpp * * Created on: 2014.7.17 * Author: spike */ /*eclipse c

背包问题 (记忆化搜索)

使用记忆化搜索,可大大提升时间效率.... 1 int n,W;//n为重量 2 int w[MAX],v[MAX]; 3 int dp[MAX][MAX]; 4 5 //从第i个物品开始挑选总重小于j的部分 6 int rec(int i,int j) 7 { 8 //记忆化搜索 9 /*if(dp[i][j] >= 0){ 10 return dp[i][j]; 11 }*/ 12 int rec; 13 if(i==n){ 14 //已经没有剩余物品了 15 rec=0; 16 } 17

UVA_437_The_Tower_of_the_Babylon_(DAG上动态规划/记忆化搜索)

描述 https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=378 n种方块,给出每一种的长宽高,现在要落起来,上面的方块的长和宽要严格小于下面的方块,问最多落多高. ACM Contest Problems ArchiveUniversity of Valladolid (SPAIN)437 The Tower of BabylonPerhap

LightOJ1417 Forwarding Emails(强连通分量+缩点+记忆化搜索)

题目大概是,每个人收到信息后会把信息发给他认识的一个人如此下去,问一开始要把信息发送给谁这样看到信息的人数最多. 首先找出图中的SCC并记录每个SCC里面的点数,如果传到一个SCC,那么里面的人都可以看到信息. 然后SCC缩点后就形成DAG,直接记忆化搜索,d(u)搜索从u点出发开始传最多能传多少人. 最后就是找答案了. 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namesp

bzoj 1589: [Usaco2008 Dec]Trick or Treat on the Farm 采集糖果【tarjan+记忆化搜索】

对这个奇形怪状的图tarjan,然后重新连边把图变成DAG,然后记忆化搜索即可 #include<iostream> #include<cstdio> using namespace std; const int N=100005; int n,a[N],h[N],cnt,dfn[N],low[N],tot,s[N],top,bl[N],si[N],col,mp[N]; bool v[N]; struct qwe { int ne,to; }e[N]; int read() { i

POJ_1088_(dp)(记忆化搜索)

滑雪 Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 95792   Accepted: 36322 Description Michael喜欢滑雪百这并不奇怪, 因为滑雪的确很刺激.可是为了获得速度,滑的区域必须向下倾斜,而且当你滑到坡底,你不得不再次走上坡或者等待升降机来载你.Michael想知道载一个区域中最长底滑坡.区域由一个二维数组给出.数组的每个数字代表点的高度.下面是一个例子 1 2 3 4 5 16 17

【bzoj1415】[Noi2005]聪聪和可可 期望记忆化搜索

题目描述 输入 数据的第1行为两个整数N和E,以空格分隔,分别表示森林中的景点数和连接相邻景点的路的条数. 第2行包含两个整数C和M,以空格分隔,分别表示初始时聪聪和可可所在的景点的编号. 接下来E行,每行两个整数,第i+2行的两个整数Ai和Bi表示景点Ai和景点Bi之间有一条路. 所有的路都是无向的,即:如果能从A走到B,就可以从B走到A. 输入保证任何两个景点之间不会有多于一条路直接相连,且聪聪和可可之间必有路直接或间接的相连. 输出 输出1个实数,四舍五入保留三位小数,表示平均多少个时间单