0-1背包

  • 描述:N件物品,第i件的重量是w[i],价值v[i]。有一个容量为W的背包,求将哪些物品放入背包可使总价值最大。每件物品可以用0或1次
  • 分析:根据题意,可以写出表达式:

\[max(\Sigma v_ix_i), s.t. \Sigma w_ix_i<=W, x_i\in\{0, 1\}
\]

最直接的思路就是:对于每件物品,都有yes/no两种选择,尝试所有的组合,记录每个组合的价值,选出满足重量条件的最大价值。时间复杂度\(O(2^n)\),空间复杂度\(O(n)\)。

// backtracking
class knapsack01 {
public:
int knapsack(int W, vector<int>& w, vector<int>& v, string& ans) {
string cur(w.size(), '0'); dfs(0, 0, 0, W, w, v, cur, ans);
return maxV;
}
private:
void dfs(int s, int curW, int curV, int W, vector<int>& w, vector<int>& v, string& cur, string& ans) {
// 到达叶子结点,得到一个解,所以在这里更改最终结果
if (s >= w.size()) {
if (maxV < curV) {
ans.assign(cur);
maxV = curV;
}
return;
} // as for goods s, two choices
for (int i = 0; i < 2; ++i) {
cur[s] = i + '0'; if (curW + i * w[s] <= W) {
curW += i * w[s];
curV += i * v[s];
dfs(s + 1, curW, curV, W, w, v, cur, ans);
curW -= i * w[s];
curV -= i * v[s];
}
}
} int maxV = 0;
};

上面的程序可以通过剪枝进行优化,下来换一种思路:

dp[i][j]表示前i件物品重量恰好为j时具有的最大价值,问题转化为求dp[N][0...W]的最大值,边界条件dp[0...N][0]=0

假设3件物品,\(w=\{1,1,2\}\),\(v=\{1,2,4\}\),\(W=2\),先用递归形式分析,每件物品只有yes/no两种状态:



可以看到,求解过程中有很多重叠子问题,故可以采用记忆化递归求解,时间复杂度即为子问题数量\(O(NW)\),空间复杂度\(O(NW)\)。

记忆化递归可以写成自底向上的动态规划,状态转移方程:

\[dp[i][j]=max\{dp[i-1][j], v[i]+dp[i-1][j-w[i]]\}
\]

// dp->space complexity O(NW)
class knapsack01 {
public:
int knapsack(int W, vector<int>& w, vector<int>& v) {
const int N = w.size();
vector<vector<int>> dp(N + 1, vector<int>(W + 1, 0));
for(int i = 1;i <= N;++i)
for (int j = w[i - 1]; j <= W; ++j) {
dp[i][j] = max(dp[i - 1][j], v[i - 1] + dp[i - 1][j - w[i - 1]]);
} return *max_element(dp[N].begin(), dp[N].end());
}
};

前i件物品只依赖于前i-1件物品,\(dp\)数组的更新方向为:



所以可以使用滚动数组降低空间复杂度为\(O(W)\):

// dp->space complexity O(W)
// method 1: use temp array
class knapsack01 {
public:
int knapsack(int W, vector<int>& w, vector<int>& v) {
const int N = w.size();
vector<int> dp(W + 1, 0);
for (int i = 1; i <= N; ++i) {
vector<int> temp(W + 1, 0);
for (int j = w[i - 1]; j <= W; ++j) {
temp[j] = max(temp[j], v[i - 1] + dp[j - w[i - 1]]);
}
dp.swap(temp);
} return *max_element(dp.begin(), dp.end());
}
}; // method 2: use scrolling array
class knapsack01 {
public:
int knapsack(int W, vector<int>& w, vector<int>& v) {
const int N = w.size();
vector<int> dp(W + 1, 0);
for (int i = 1; i <= N; ++i) {
// iterate j reversely, avoid dp override
for (int j = W; j >= w[i - 1]; --j) {
dp[j] = max(dp[j], v[i - 1] + dp[j - w[i - 1]]);
}
} return *max_element(dp.begin(), dp.end());
}
};

完全背包

  • 每件物品可以使用任意多次
  • 一个Naive的思路: 虽然题目描述每件物品可以使用任意多次,但实际上由于W的限制,每件物品最多使用\(\lfloor W/w[i] \rfloor\)次。这样我们可以将每件物品拆为\(\lfloor W/w[i] \rfloor\)件,问题就转化为了0-1背包。子问题仍然有NW个,但是求解每个子问题需要\(O(W/w[i])\),总的时间复杂度\(O(\Sigma (W/w[i])*W)\),也即\(O(W*拆后物品件数)\)。
  • 更tricky的做法:W无法改变,只能改变拆后物品件数。这里可以使用二进制的思想:假设我们某件物品可以使用\(10=8+2\)次,原本需要复制出10件,现在只要复制出2件,价值和重量是原来的8倍和2倍,这样就降低了复杂度。
  • 完全背包有\(O(NW)\)的算法。

多重背包

  • 每件物品最多可以使用\(num[i]\)次。
  • 同样,Naive的思路就是将每件物品都复制\(num[i]\)次,问题转化为0-1背包,复杂度\(O(\Sigma nums[i]*W)\)。
  • 将\(num[i]\)用二进制表示,价值和重量变为原来的相应倍,降低复杂度。

Future

后续还有混合背包、二维费用的背包等,详情可以学习背包九讲

Knapsack Problem的更多相关文章

  1. knapsack problem 背包问题 贪婪算法GA

    knapsack problem 背包问题贪婪算法GA 给点n个物品,第j个物品的重量,价值,背包的容量为.应选哪些物品放入包内使物品总价值最大? 规划模型 max s.t. 贪婪算法(GA) 1.按 ...

  2. FZU 2214 Knapsack problem 01背包变形

    题目链接:Knapsack problem 大意:给出T组测试数据,每组给出n个物品和最大容量w.然后依次给出n个物品的价值和体积. 问,最多能盛的物品价值和是多少? 思路:01背包变形,因为w太大, ...

  3. [DP] The 0-1 knapsack problem

    Give a dynamic-programming solution to the 0-1 knapsack problem that runs in O(nW) time, where n is ...

  4. 对背包问题(Knapsack Problem)的算法探究

    对背包问题(Knapsack Problem)的算法探究 至繁归于至简,这次自己仍然用尽可能易理解和阅读的解决方式. 1.问题说明: 假设有一个背包的负重最多可达8公斤,而希望在背包中装入负重范围内可 ...

  5. 动态规划法(四)0-1背包问题(0-1 Knapsack Problem)

      继续讲故事~~   转眼我们的主人公丁丁就要离开自己的家乡,去大城市见世面了.这天晚上,妈妈正在耐心地帮丁丁收拾行李.家里有个最大能承受20kg的袋子,可是妈妈却有很多东西想装袋子里,已知行李的编 ...

  6. FZU 2214 ——Knapsack problem——————【01背包的超大背包】

    2214 Knapsack problem Accept: 6    Submit: 9Time Limit: 3000 mSec    Memory Limit : 32768 KB  Proble ...

  7. FZU-2214 Knapsack problem(DP使用)

    Problem 2214 Knapsack problem Accept: 863    Submit: 3347Time Limit: 3000 mSec    Memory Limit : 327 ...

  8. 0/1 knapsack problem

    Problem statement Given n items with size Ai and value Vi, and a backpack with size m. What's the ma ...

  9. FZU - 2214 Knapsack problem 01背包逆思维

    Knapsack problem Given a set of n items, each with a weight w[i] and a value v[i], determine a way t ...

  10. (01背包 当容量特别大的时候) Knapsack problem (fzu 2214)

    http://acm.fzu.edu.cn/problem.php?pid=2214   Problem Description Given a set of n items, each with a ...

随机推荐

  1. IIC驱动学习笔记,简单的TSC2007的IIC驱动编写,测试

    IIC驱动学习笔记,简单的TSC2007的IIC驱动编写,测试 目的不是为了编写TSC2007驱动,是为了学习IIC驱动的编写,读一下TSC2007的ADC数据进行练习,, Linux主机驱动和外设驱 ...

  2. 【php】面向对象(五)

    一. 类型约束: a) 约束函数可传入的参数类型二. 类的遍历 a) Foreach b) 可以将类当中的所有成员属性遍历出来三. 关于操作类与对象的一些函数: a) 判断函数 i. Function ...

  3. Hadoop(二)搭建Hadoop

    0.部署计划 本文使用的版本是 red hat 6.8 -本来想用Centos7搭建的,但是工作需要还是换成这个了,不用红帽子用Centos 6系列的应该也可以 JDK 1.8 Hadoop 2.7. ...

  4. android学习相关intent和fragment的先关知识点

    对于使用intent,主要是用来进行活动之间的跳转,然后就是通过intent向下一个活动传递数据,还有就是想上一个活动传递数据. 实例: 先添加按钮的点击事件,当点击按钮时进行事件的触发,主要代码是 ...

  5. Django常用的第三方包

    Django常用的第三方包 API开发 djangorestframework django-rest-multiple-models django-cors-headers 查询 django-fi ...

  6. Tcl编成第二天,set与unset

    代码如下: #!/usr/bin/tclsh set value "one" puts $value unset value puts $value set表示创建一个变量第一个参 ...

  7. MODIS系列之NDVI(MOD13Q1)二:modis数据相关信息

    1.MODIS数据的特点 (1)全球免费:NASA对MODIS数据实行全球免费接收的政策(TERRA卫星除MODIS外的其他传感器获取的数据均采取公开有偿接收和有偿使用的政策),这样的数据接收和使用政 ...

  8. redis 正确实现分布式锁的正确方式

    前言 最近在自己所管理的项目中,发现redis加锁的方式不对,在高并发的情况有问题.故在网上找搜索了一把相关资料.发现好多都是互相抄袭的,很多都是有缺陷的.好多还在用redis 的 setnx命令来实 ...

  9. EL表达式---自定义函数(转)

    EL表达式---自定义函数(转) 有看到一个有趣的应用了,转下来,呵呵!! 1.定义类MyFunction(注意:方法必须为 public static) package com.tgb.jstl;  ...

  10. 关于TOMCAT中的两个Web.xml

    关于TOMCAT中的两个Web.xml (2013-01-19 17:32:57) 转载▼ 标签: 杂谈   初学JAVA web开发.. Servlet定义的时候,我发现在${catalina.ho ...