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. LInux文件管理篇,权限管理

    一: chgrp 改变文件所属用户组 chown 改变文件所有者 注意: 1.使用格式 chgrp/chown     user      file eg: chgrp lanyue permissi ...

  2. 使用webview查找元素,appium查找web页面元素

    9 webview查找元素 9.1 webview 简介 WebView是android中一个非常重要的控件,它的作用是用来展示一个web页面.它使用的内核是webkit引擎,4.4版本之后,直接使用 ...

  3. alg-最长公共子序列

    class Solution { public: std::string LongestCommonSubsequence(const std::string& s1, const std:: ...

  4. 如何使用Swagger-UI在线生成漂亮的接口文档

    一.简单介绍 Swagger是一个实现了OpenAPI(OpenAPI Specification)规范的工具集.OpenAPI是Linux基金会的一个项目,试图通过定义一种用来描述API格式或API ...

  5. Loading纯JS

    function showLoading() { document.getElementById("over").style.display = "block" ...

  6. L20 梯度下降、随机梯度下降和小批量梯度下降

    airfoil4755 下载 链接:https://pan.baidu.com/s/1YEtNjJ0_G9eeH6A6vHXhnA 提取码:dwjq 梯度下降 (Boyd & Vandenbe ...

  7. summary && scenarios

    小组中期总结 项目目标: 我们的目标是制作一款pdf阅读器的必应查词插件,帮助大家在阅读英文资料的时候查单词更加的方便并且可以帮助人们英语学习. 痛点:对于在电脑上现在屏幕取词软件已经做得相当好了,所 ...

  8. el-tab-pane label的文字内容怎样设间距

    el-tab-pane label的文字内容怎样设间距 问题描述: 在使用element-ui的el-tab-pane做标签页时,label属性的位置与样式不能通过style样式直接解决 百度后几乎没 ...

  9. Python父类和子类关系/继承

    #!/usr/bin/env python # -*- coding: utf-8 -*- """ @File:继承_子类和父类的关系.py @E-mail:364942 ...

  10. Navicat自动备份数据库

    @ 目录 Navicat自动备份数据库 备份与还原 修改备份位置 MySQL:5.7 Navicat:11 Windows10 重要数据库的定时备份是非常重要的,使用Navicat可以非常方便快捷地自 ...