有n个物品和购物车的容量,每个物品的重量为w[i],价值为v[i],购物车容量为W。选若干个物品放入购物车中,使价值最大。重量不能够超过购物车容量。
原问题约数条件和目标函数如下:
子问题的约数条件和目标函数如下:
我们只需要证明:原问题的解去掉,x1为子问题的最优解。即证明最优子问题结构的性质。
(1)假设已经知道了X={x1,x2,...,xn}是原问题{a1,a2,...,an}的最优解,那么原问题去掉第一个物品就变成子问题{a2,a3,...,an},则X'={x2,x3,...,xn}是子问题的最优解
(2)通过最优子结构性质建立最优值递归式
可以对每个物品依次检查是否放入或者不放入。
用c[i][j]表示前i件物品放入一个容器为j的购物车可以获得的最大价值。
不放入第i件物品,xi=0,购物车的价值不增加,那么子问题就转化为“前i-1件物品放入容量为j的背包中”,最大价值为c[i-1][j]。
放入第i件物品,xi=1,装入购物车的价值增加了vi。
扫描二维码关注公众号,回复:
3001059 查看本文章
以上是问题转化的数学模型。wi是第i件商品的总重量,j是购物车的容量
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <stdio.h>
#include <algorithm>
using namespace std;
int main()
{
freopen("D:/project/data.csv", "r", stdin);
int W, n;//背包容量,n为商品数量
cin >> W >> n;
vector<int> w(n+1,0),v(n+1,0);//商品容量和商品价值
for (int i = 1; i <= n; i++)
cin >> w[i];
for (int i = 1; i <= n; i++)
cin >> v[i];
vector<int> x(n + 1, 0);//确定第i个到第n个商品是否放入
//下标从0开始避免了初始化
vector<vector<int>> dp(n+1,vector<int> (W+1,0));//背包容量0-W
for(int i=1;i<= n;i++)//商品编号从0-(n-1),那还需要把编号为1的初始化
for (int j = 1; j <= W; j++)//容量为0,可装为0
{
if (j >= w[i])
{
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w[i]] + v[i]);
}
else
dp[i][j] = dp[i-1][j];
}
cout << dp[n][W] << endl;
//逆向构造最优解
int j = W;
for (int i = n; i > 0; i--)
if (dp[i][j] > dp[i - 1][j])//确定第i个商品是放入的
{//如果相等,比较w[i]
x[i] = 1;
j -= w[i];
}
for (int i = 1; i <= n; i++)
if(x[i]==1)
cout << i << " ";
cout << endl;
return 0;
}
以上这种情况下,虽然能够找到背包的最大价值。但是对于商品的选择不能够保证,价值相同时,选择重量小的商品。
如:背包重量W=10,商品数量n=5 ,w={5,7,7,4,3}; v={11,11,11,11,11}。最大价值应该是22,但是应该优先选择第4号,第5号商品。以上方法只能选择到第1号,第4号商品。一下方法可以解决。
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <stdio.h>
#include <algorithm>
using namespace std;
typedef struct node
{
int i;//原序号
int w;
int v;
}node;//node数组表示的先序号
int main()
{
freopen("D:/project/data.csv", "r", stdin);
int W, n;//背包容量,n为商品数量
cin >> W >> n;
vector<int> w(n + 1, 0), v(n + 1, 0);//商品容量和商品价值
for (int i = 1; i <= n; i++)
cin >> w[i];
for (int i = 1; i <= n; i++)
cin >> v[i];
//使用直接插入排序
vector<node> w_node;
node node;
node.i = 0;
node.w = 0; node.v = 0;
w_node.push_back(node);
for (int i = 1; i <= n; i++)//原数组下标
{
node.i = i;//原下标
node.w = w[i]; node.v = v[i];
int count = 0;
for (int j = 0; j < i; j++)//先数组下标
{
if (node.w < w_node[j].w)
{
w_node.insert(w_node.begin() + j, node);
break;
}
count++;
}
if(count==i)
w_node.push_back(node);
}
// for (int i = 1; i <= n; i++)
// cout << w_node[i].i << " " << w_node[i].w << " " << w_node[i].v << endl;
vector<vector<int>> dp(n + 1, vector<int>(W + 1, 0));//背包容量0-W
for (int i = 1; i <= n; i++)//商品编号从0-(n-1),那还需要把编号为1的初始化
for (int j = 1; j <= W; j++)//容量为0,可装为0
{
if (j >= w_node[i].w)//相同价值是否选择质量小的?
{
dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - w_node[i].w] + w_node[i].v);//如果相等,就比较质量重新分配dp[i-1][j]
}//相邻可以比w,不相邻比不了。所以只能够排序
else
dp[i][j] = dp[i - 1][j];
}
cout << dp[n][W] << endl;
//逆向构造最优解
int j = W;
vector<int> x(n + 1, 0);//确定第i个到第n个商品是否放入
for (int i = n; i > 0; i--)
if (dp[i][j] > dp[i - 1][j])//确定第i个商品是放入的
{
x[i] = 1;
j -= w[i];
}
for (int i = 1; i <= n; i++)
if (x[i] == 1)
cout << w_node[i].i << " ";
cout << endl;//如果输出需要按序,则将其放入容器中排序下就好
return 0;
}