01背包问题的解决

测试案例:http://poj.org/problem?id=3624

需求:0-1背包问题(下面代码在poj上不是超时就是内存消耗过大,毕竟算法的时间复杂度是O(nW))
xj =01的背包问题
输入格式:
行数	内容
1:		N M	(种类,最大重量)
2~1+N:	W_i D_i	(单一重量,价值)
状态转移方程:f(i,j) = max(f(i,j),f(i-1,j-ui)+wi)
例如:
		输入:
		4 6
		1 4
		2 6
		3 12
		2 7
		输出:
		23

1、二维数组动态规划

1.1、Runtime error的版本

状态转移方程:f(i,j) = max(f(i,j),f(i-1,j-ui)+wi)

#include<iostream>
#include<math.h>
#define MAX_N 3402		// 最大种类数
#define MAX_W 400		//最大单一种类重量
#define MAX_D 100		// 最大价值
#define MAX_M 12880		//最大限重
using namespace std;

 //dp[k][y] 表示在前k中物品中选择,背包重量不超过y时背包最大价值
int dp[MAX_N+1][MAX_M+1];
int weights[MAX_N+1] ;
int values[MAX_N+1] ;

int main() {
	int n,m,i,j;
	cin >> n >> m;
	for (i = 1; i <= n; i++) {
		cin >> weights[i] >> values[i];
	}
	// (1)初始化
	for (i = 0; i <= m; i++) {
		dp[0][i] = 0;	/*不买东西,装入重量再多的非有价值的情况*/
	}
	for (i = 0; i <= n; i++) {
		dp[i][0] = 0;	/*多种东西无价值*/
	}

	// (2)动态规划
	for (i = 1; i <= n; i++) {	//对前i个物品
		for (j = 1; j <= m; j++) {//对前j个重量
			// 在 同个重量级的前i-1个种类组合 和 在(j-weights[i])的情况下利用前k种物品转入获取最大值再加当前物品价值 中选择
			if (j < weights[i])		// 这时候背包容量不足以放下第 i 件物品,只能选择不拿,继续考虑前i-1件物品
				dp[i][j] = dp[i - 1][j];
			else					//这时背包容量可以放下第 i 件物品,我们可以考虑拿或者不拿,分析怎样价值更大:
				dp[i][j] = max(dp[i - 1][j], dp[i-1][j- weights[i]] + values[i]);
		} 
	}
	cout << dp[n][m] << endl;
}

虽然这个算法在公式上没有错误,但是使用的多行是多余的,只需要一行保存就可以,不过,这个版本是《算法设计与分析》课本教的,可能是为了好理解吧

1.2 展示路径


#include<iostream>
#include<math.h>
#define MAX_N 3402		// 最大种类数
#define MAX_W 400		//最大单一种类重量
#define MAX_D 100		// 最大价值
#define MAX_M 12880		//最大限重
using namespace std;

 //dp[k][y] 表示在前k中物品中选择,背包重量不超过y时背包最大价值
 //		不装第k个物品或者至少装一个第k种物品
int dp[MAX_N+1][MAX_M+1];
int I[MAX_N + 1][MAX_M + 1]; //标志数组,方便回溯,计算优化函数值dp[k][y]时用到物品的最大编号
int weights[MAX_N+1] ;
int values[MAX_N+1] ;

void TrackSolution(int n,int m) {
	int x[MAX_N+1] = { 0 };	//n中商品装入量
	int y = m, j = n;	// I[J][y]
	while (I[j][y] != 0) {
		j = I[j][y];
		while (I[j][y] == j) {
			y = y - weights[j];
			x[j]++;
		}
	}
	for (int i = 1; i <= n; i++) {
		cout << x[i] << " ";
	}
	cout << endl;
}

int main() {
	int n,m,i,j;
	cin >> n >> m;
	for (i = 1; i <= n; i++) {
		cin >> weights[i] >> values[i];
	}
	// (1)初始化
	for (i = 0; i <= m; i++) {
		dp[0][i] = 0;	/*不买东西,装入重量再多的非有价值的情况*/
	}
	for (i = 0; i <= n; i++) {
		dp[i][0] = 0;	/*多种东西无价值*/
	}

	// (2)动态规划
	for (i = 1; i <= n; i++) {	//对前i个物品
		for (j = 1; j <= m; j++) {//对前j个重量
			// 在 同个重量级的前i-1个种类组合 和 在(j-weights[i])的情况下利用前k种物品转入获取最大值再加当前物品价值 中选择
			if (j < weights[i])
				dp[i][j] = dp[i - 1][j];
			else 
				dp[i][j] = max(dp[i - 1][j], dp[i-1][j- weights[i]] + values[i]);

			if (j < weights[i] || (dp[i - 1][j] > dp[i][j - weights[i]] + values[i]))
				I[i][j] = I[i - 1][j];
			else
				I[i][j] = i;
		} 
	}
	cout << dp[n][m] << endl;
	TrackSolution(n, m);	
}

在这里插入图片描述

2、回溯法

2.1 、Time Limit Exceeded

#include<stdio.h>
#define MAX_M 12880		//最大限重
#define MAX_N 3402		// 最大种类数
int c;//背包容量
int n;//物品种类数量
int weight[MAX_N+1];	//存放n个物品种类的数组
int price[MAX_N+1];		//存放价值的数组
int currentWeight = 0;	//当前重量
int currentPrice = 0;	//当前价值
int bestPrice = 0;		//当前最优值
int bp = 0;				//全局最优价值

void Backtracking(int i) {
	if (i > n) {
		if (bestPrice > bp) {
			bp = bestPrice;
		}
		return;
	}
	if (currentWeight + weight[i] <= c) {
		// 把物品i放入背包,搜索左子树
		currentWeight += weight[i];
		bestPrice += price[i];
		Backtracking(i + 1);	
		currentWeight -= weight[i];
		bestPrice -= price[i];
	}
	Backtracking(i + 1);
}


int main() {
	int i;
	scanf_s("%d%d", &n,&c);
	for (i = 1; i <= n; i++) {
		scanf_s("%d%d", &weight[i], &price[i]);
	}
	Backtracking(1);
	printf("%d\n", bp);
}

2.2 展示路径

#include<stdio.h>
#define MAX_M 12880		//最大限重
#define MAX_N 3402		// 最大种类数
int c;//背包容量
int n;//物品种类数量
int weight[MAX_N+1];	//存放n个物品种类的数组
int price[MAX_N+1];		//存放价值的数组
int currentWeight = 0;	//当前重量
int currentPrice = 0;	//当前价值
int bestPrice = 0;		//当前最优值
int bestAnswer[MAX_M];	//当前最优解
int bp = 0;				//全局最优价值
int bA[MAX_M+1];			//全局最优路径

void Backtracking(int i) {
	if (i > n) {
		if (bestPrice > bp) {
			bp = bestPrice;
			for (int j = 1; j <= n; j++) {
				bA[j] = bestAnswer[j];
			}
		}
		return;
	}
	if (currentWeight + weight[i] <= c) {
		// 把物品i放入背包,搜索左子树
		bestAnswer[i] = 1;
		currentWeight += weight[i];
		bestPrice += price[i];
		Backtracking(i + 1);	
		currentWeight -= weight[i];
		bestPrice -= price[i];
	}
	bestAnswer[i] = 0;
	Backtracking(i + 1);
}


int main() {
	int i;
	scanf("%d%d", &n,&c);
	for (i = 1; i <= n; i++) {
		scanf("%d%d", &weight[i], &price[i]);
	}
	Backtracking(1);
	printf("%d\n", bp);
	for (i = 1; i <= n; i++)
		printf("%d ", bA[i]);
}

在这里插入图片描述

3、一维数组动态规划 AC

注意,for (int j = m; j >= weights[i]; j--)是逆序遍历总重量

// 一维数组解法
#include<iostream>
#define MAX_M 12880		//最大限重
#define MAX_N 3402		// 最大种类数
using namespace std;

int dp[MAX_M + 1];
int weights[MAX_N + 1];
int values[MAX_N + 1];

int main() {
	int n, m, i, j;
	cin >> n >> m;
	for (i = 1; i <= n; i++) {
		cin >> weights[i] >> values[i];
	}
	for (int i = 1; i <= n; i++) {	// 种类
		for (int j = m; j >= weights[i]; j--) {	//容量
			dp[j] = max(dp[j], dp[j - weights[i]] + values[i]);
		}
	}
	cout << dp[m] << endl;
}

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

广大菜鸟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值