0_1背包问题

有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;
}



猜你喜欢

转载自blog.csdn.net/weixin_37703648/article/details/78599254
今日推荐