01背包问题定义:给定n种物品和一背包。物品i的重量是wi,其价值是vi,背包的容量是c.问如何选择装入背包中的物品,使得装入背包中的物品的总价值最大?背包的容量有
限,被加入到背包里的物品的重量之和要小于或等于c,其次是每个物品不能分割,要么装入要么不装入。其动态转化方程为:
f[i,j] = Max{ f[i-1,j-Wi]+Pi( j >= Wi ), f[i-1,j] }
</pre><pre name="code" class="cpp">#include<iostream>
#include<vector>
#include<list>
#include<fstream>
#include<string>
#include<set>
#include<map>
#include<algorithm>
#include<stack>
using namespace std;
ofstream fout("c:users\\dell\\desktop\\data.txt");
int min_weight = INT_MAX;
int get_max_value(vector<int>&weight,vector<int>&value,int vol){
int size = value.size();
vector<int>row(vol+1,0);//行表示物品的个数
vector<vector<int>>info;
for (int i = 0; i < size + 1; i++)info.push_back(row);//新建二维表
//开始填表
int max_value = 0;
for (int i = 1; i < size + 1; i++){/
if (weight[i - 1] < min_weight)min_weight = weight[i-1];
for (int j =min_weight; j < vol + 1; j++){
if (j>=weight[i - 1]){
info[i][j] = max(info[i - 1][j], info[i - 1][j - weight[i - 1]] + value[i - 1]);
if (info[i][j]>max_value)max_value = info[i][j];
}
else{//当小于第i件物品的重量时,直接将上一行的元素向下移动,只有当前容量和当前处理物品的重量相同时才可能出现新的最大价值量
info[i][j] = info[i-1][j];
if (info[i][j]>max_value)max_value = info[i][j];
}
}
}
//打印
for (int i = 0; i < size + 1; i++){//控制行
for (int j = 0; j < vol + 1; j++){
fout << info[i][j] << " ";
}
fout << endl;
}
return max_value;
}
int main(){
vector<int>weight = {2,2,6,2,1};
vector<int>value = {6,3,5,8,3};
int vol = 10;
int re = get_max_value(weight,value,vol);
cout << re << endl;
return 0;
}
vector<int>weight = {2,2,6,2,1};
vector<int>value = {6,3,5,8,3};
其中红色表示在前4件物品中,在背包容量为6的情况下最大价值量为17(分别是6+3+8)。
拓展:leetcode:416. Partition Equal Subset Sum
Given a non-empty array containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal.
Note:
- Each of the array element will not exceed 100.
- The array size will not exceed 200.
Example 1:
Input: [1, 5, 11, 5] Output: true Explanation: The array can be partitioned as [1, 5, 5] and [11].
Example 2:
Input: [1, 2, 3, 5] Output: false Explanation: The array cannot be partitioned into equal sum subsets.这道题的意思就是在数组中挑任意个元素,使得所挑元素的和与剩下的元素的和相同。
第一种方法:
假设数组的元素的个数是n,利用树的结构来解决,对于数组中的每个元素只有取和不取两种情况,所以可以构建一颗深度为n+1的二叉树(不是真的构造,而是通过递归函数调
用实现),向左代表选择当前物品,向右代表不选择当前物品。在函数调用的过程中,可能会有剪枝操作!!!
第二种方法:
就是利用上述01背包的思想,将背包的容量设置为数组所有元素的和的一半(如果数组的和不是偶数则不可能平分)。在这种情况下,每个物品的总量和价值量是1:1的情况,
一旦背包刚好被装满,就是二维数组的最后一个元素的值等于sum/2。可以知道二维数组的最后一个元素的值是小于或等于sum/2的,如果利用01背包思想求得的最大价值量
都小于sum/2那么这个数组不可能存在一个子序列的和刚好等于sum/2.
源码如下:
class Solution {
public:
bool canPartition(vector<int>& nums) {
int sum = 0;
bool flag;
for (int i = 0; i<nums.size(); i++) sum += nums[i];
if (sum % 2 == 1) return false;
else{
int m = nums.size();
int n = sum / 2;
vector<int>value = nums;
int **f = new int*[m];
for (int i = 0; i < m; i++){
f[i] = new int[n + 1];
memset(f[i], 0, sizeof(int)*(n + 1));
}
for (int i = nums[0]; i <= n; i++){
f[0][i] = value[0];
}
for (int i = 1; i<m; i++){
for (int j = nums[i]; j <= n; j++){
f[i][j] = max(f[i - 1][j], f[i - 1][j - nums[i]] + value[i]);
}
}
if (f[m - 1][n] == n)flag = true;
else flag = false;
for (int i = 0; i < m; i++)delete[]f[i];
delete[]f;
return flag;
}
}
};
Leetcode上现实内存超超出限制,一直没找出原因来,求大牛指点!!!!
第三种思想:
将数组中任意个元素的和全部列出来(即任意从1到n个个元素之和),将这和全部存放起来,如果这些和都和sum/2不相等,则不存在这样的非空子序列使得它的和是数组所有
元素和的一半!!!!