0-1背包问题与数组分割问题
来源:互联网 发布:手机淘宝网银支付 编辑:程序博客网 时间:2024/06/11 16:46
什么是0-1背包问题?
给定两个数组weight[N]和value[N]分别表示物品的重量和价值,现在有一个容量为s的背包,要求在背包中放置若干件物品使得物品的总价值最大。
怎么解决?
采用动态规划,令dp[i][j]表示在前i件产品中做选择且背包中物品重量小于等于j时所选择的物品的最大价值,显然第i件物品的选还是不选完全取决于之前的状态,状态转移方程为dp[i][j] = max(dp[i-1][j], dp[i-1][j-weight[i]] + value[i]),如果dp[i][j] == dp[i-1][j]说明不选择第i件物品,背包中物品的重量仍然为j;如果dp[i][j] == dp[i-1][j-weight[i]] + value[i]说明选择第i件物品,背包重量增加了weight[i]且物品总价值增加了value[i]。最终的返回值为dp[N][s]。
没有进行空间优化的代码:
int knapsackSolve1(const vector<int>& weight, const vector<int>& value, int s){if (weight.size() != value.size()){return 0;}int n = weight.size();vector<vector<int> > dp(n, vector<int>(s + 1, 0));for (int i = 1; i < n; ++i){for (int j = weight[i]; j <= s; ++j){if (dp[i - 1][j - weight[i]] + value[i] > dp[i - 1][j])dp[i][j] = dp[i - 1][j - weight[i]] + value[i];elsedp[i][j] = dp[i - 1][j];}}return dp[n - 1][s];}
进行空间优化的代码:
int knapsackSolve2(const vector<int>& weight, const vector<int>& value, int s){if (weight.size() != value.size()){return 0;}int n = weight.size();vector<int> dp(s + 1, 0);for (int i = 1; i < n; ++i){for (int j = s; j >= weight[i]; --j)//可以节省空间,j必须从大到小{//dp[j]是本轮的,dp[j - weight[i]]是上一轮的,所以必须保证dp[j]在dp[j - weight[i]]之前更新if (dp[j - weight[i]] + value[i] > dp[j]){dp[j] = dp[j - weight[i]] + value[i];}}}return dp[s];}
用背包思想解决数组分割问题:
有一个无序的、元素个数为2n的正整数数组,要求把这个数组分成两个长度为n的子数组,并使两个子数组的和最接近。
用dp[i][j][c]来表示前面i个元素取j个且这j个元素和不大于c(可以认为是背包的容量)的最佳方案(也就是最接近c的方案)。如果第i个元素不取,那么dp[i][j][c] = dp[i-1][j][c];如果第i个元素被选取,那么dp[i][j][c] = dp[i-1][j-1][c-a[i]]+a[i],因此可知动态规划的递推式为dp[i][j][c] = max(dp[i-1][j-1][c-a[i]]+a[i], dp[i-1][j][c])。
代码已经进行空间优化
int divideArray(const vector<int>& array)//已经去除最外围空间{int n;if (array.size() % 2 != 0){return 0;}else{n = array.size() / 2;}int sum = std::accumulate(array.begin(), array.end(), 0);vector<vector<int>> dp(n + 1, vector<int>(sum / 2 + 2, 0));for (int i = 1; i < array.size(); ++i){for (int j = 1; j <= i && j <= n; ++j)//最多只需要选取n个元素{for (int c = sum / 2 + 1; c >= array[i]; --c)//从大到小,为了节约最外围空间{dp[j][c] = max(dp[j - 1][c - array[i]] + array[i], dp[j][c]);}}}return dp[n][sum / 2 + 1];}
关于数组分割问题《编程之美》上有一个更好的解决方案:
维护一个二维数组,用来存储array子数组和,isOk[j][s]这个bool类型的数组表示array中是否存在j个数其和等于s。通过动态规划求出isOk[n][i]=true其中i是小于sum / 2 + 1的最大的整数。
代码如下
int divideArray2(const vector<int>& array){int n;if (array.size() % 2 != 0){return 0;}else{n = array.size() / 2;}int sum = std::accumulate(array.begin(), array.end(), 0);vector<vector<bool>> isOk(n + 1, vector<bool>(sum / 2 + 2, 0));isOk[0][0] = true;//选取0个元素其和为0这种情况肯定为真for (int i = 0; i < array.size(); ++i){for (int j = 1; j <= n && j <= i; ++j){for (int s = sum / 2 + 1; s >= array[i]; --s){if (isOk[j-1][s - array[i]]){isOk[j][s] = true;}}}}//选出最接近sum / 2 + 1的和for (int s = sum / 2 + 1; s >= 0; --s){if (isOk[n][s]){return s;}}return 0;}上面的两种方法都求出了数组分割后子数组的和,但是子数组具体包含哪些元素并不能知道,下面的代码采用深度优先搜索的方法求数组中的N个元素且它们的和为定值。
//从array中获取和为sum且元素个数为n的子序列(子序列不要求连续)bool fixedSumNSequence(const vector<int>& array, int start, int n, int sum, vector<int>& subArray){if (subArray.size() >= n){if (sum == 0){return true;}else{return false;}}for (int i = start; i < array.size(); ++i){subArray.push_back(array[i]);if (fixedSumNSequence(array, i + 1, n, sum - array[i], subArray)){return true;}subArray.pop_back();}return false;}bool fixedSumNSequence(const vector<int>& array, int n, int sum, vector<int>& subArray){if (n >= array.size()){return false;}return fixedSumNSequence(array, 0, n, sum, subArray);}
测试用例:
int main(){vector<int> weight = { 0, 10, 20, 30 };//在数组前面加上0元素是为了简便代码(动态规划的边界好确定)vector<int> value = { 0, 60, 100, 120 };cout << knapsackSolve1(weight, value, 50) << endl;cout << knapsackSolve2(weight, value, 50) << endl;vector<int> array = { 0, 1, 5, 7, 8, 9, 6, 3, 11, 20, 17, 12 };cout << divideArray(array) << endl;vector<int> subArray;if (fixedSumNSequence(array, array.size() / 2, divideArray2(array), subArray)){for (auto p : subArray){cout << p << " ";}cout << endl;}return 0;}
- 0-1背包问题与数组分割问题
- 从数组分割到背包问题(2)
- 编程之美2.18_由数组分割到背包问题(1)
- 0-1背包问题与分数背包问题
- 数组分割问题
- 数组分割问题
- 数组分割问题
- 【算法导论】0-1背包问题 与 部分背包
- 【算法导论】0-1背包问题 与 部分背包
- 算法与数据结构 - 0/1背包问题
- 动态规划与0-1背包问题
- 背包问题和0-1背包问题
- 背包问题和0-1背包问题
- 背包问题系列--"0-1背包问题"
- 背包笔记-含0/1背包问题、完全背包问题、多重背包问题、二维背包问题、分组背包问题
- nyoj 106 背包问题 可分割
- 背包问题(物体可分割)
- 贪心法之可分割背包问题
- TcpFlow 1.4.6( C++版本)源代码分析之main函数分析
- 跳槽意愿降低,最容易月薪过万城市公布
- SpringMVC学习—使用ajax返回Json出现乱码解决方法
- 途牛原创|途牛无线权限系统的架构设计与实践
- Oracle 分析函数/窗口函数
- 0-1背包问题与数组分割问题
- [1.5]以二次排序算法的实现为例体验spark高级排序
- window10 Embedded 各分支
- Bugly SDK接入指南(iOS)bug统计
- 审批-审批条件(标准单据)
- PAT乙级—1014. 福尔摩斯的约会 (20)-native
- 第15周程序阅读2
- 关于event.getX()与event.getRawX()的问题
- 【JS设计模式】解释器模式代码示例