01背包回溯

#include<iostream>
#include"backtrack01.h"
using namespace std;
int n, c, bestp;//物品数,背包容量,最大价值
int p[100], w[100], x[100], bestx[100];//物品价值,物品重量,x[i]暂存物品的选中的情况
int num = 0;
/*
算法思想:DFS+剪枝策略
本质上是对蛮力枚举的优化,优化程度较高(在实际应用中),但其最坏情况时间复杂度仍然是Ο(2^n),但一般不会,大多数的分支都被剪掉
剪左支:背包容量限制
剪右支:贪心算法求得价值上界
*/
int main(){
	bestp = 0;
	cout << "背包容量:" << endl;
	cin >> c;
	cout << "物品数" << endl;
	cin >> n;
	cout << "依次输入物品质量" << endl;
	for (int i = 1; i <= n; i++)
		cin >> w[i];
	cout << "依次输入物品价值" << endl;
	for (int i = 1; i <= n; i++)
		cin >> p[i];
	cout << "按性价比排序:" << endl;
	sort(n);//按性价比排序
	for (int i = 1; i <= n; i++)
		cout << p[i] << " " << w[i] << endl;
	backtrack(1, 0, 0);
	cout << "最大价值:" << endl << bestp << endl << "选中的物品" << endl;
	for (int i = 1; i <= n; i++)
	if (bestx[i] != 0)cout << i << " ";
	cout << endl;
	cout << "num=" << num;
	return 0;
}
void backtrack(int i, int cp, int cw)//回溯算法,i为递归层数,cp:当前价值,cw:当前重量
{
	int j;
	if (i > n){//到叶子节点 
		if (cp > bestp){//大于更新bestp,bestx[] 
			bestp = cp;
			for (int i = 0; i <= n; i++)
				bestx[i] = x[i];
		}
	}
	else{
		for (j = 1; j >= 0; --j){
			x[i] = j;
			if (cw + x[i] * w[i] <= c){
				cw += w[i] * x[i];
				cp += p[i] * x[i];
				//if (bound(i,cp,cw) <= bestp) break;
				//右子树剪枝,右子树中有可能包含最优解时才进入右子树,
				//有可能包含就是bound>bestp,价值上界大于当前最大价值,说明有可能
				backtrack(i + 1, cp, cw);
				cw -= w[i] * x[i];
				cp -= p[i] * x[i];
				num++;//记录循环次数,说明时间复杂度
			}
		}
	}
}
int bound(int i, int cp, int cw)//对剩余物品性价比进行排序,贪心选择性价比高的,返回价值上界
{
	float surplus_weight = c - cw;//剩余价值
	float bound = cp;//初值
	for (; i > n&&surplus_weight >= w[i]; i++)//对背包剩余空间,加入物品(按性价比优先)
	{
		bound += p[i];
		surplus_weight -= w[i];
	}
	if (i <= n)//贪心算法_部分背包问题,无法装入一个,装半个
	{
		bound += p[i] * surplus_weight / w[i];
	}
	//cout <<"价值上界"<< bound;
	return bound;

}
void sort(int n)//对输入的物品,按性价比排序,便于求bound
{
	float* p_w = (float*)malloc(sizeof(float)*(n + 1));
	for (int i = 1; i <= n; i++)//计算性价比
	{
		p_w[i] = p[i] * 1.0 / w[i];
		//cout << p_w[i]<<" ";
	}
	//  9 1 2 3
	for (int i = 1; i <= n + 1; i++)
	{
		for (int j = 1; j <= n - i; j++)
		{
			if (p_w[j] < p_w[j + 1])
			{
				swap(p, j, j + 1);
				swap(w, j, j + 1);
				swapf(p_w, j, j + 1);
			}
		}
	}
}
void swap(int x[], int i, int j)
{
	int price = x[i];
	x[i] = x[j];
	x[j] = price;
}
void swapf(float x[], int i, int j)
{
	float price = x[i];
	x[i] = x[j];
	x[j] = price;
}
/*
50
5
15 25 27 30 5
30 44 46 50 12


8
5
3 5 1 2 2
4 5 2 1 3

1000
10
501  725  359  465  146  828  492  943  437  605
170  479  963  706  282  962  996  828  392  903

5000
50
501  725  359  465  146  828  492  943  437  605
154  383  717  896  727  539  913  300  895  812
334  665  712  869  645  758  860  742  779  36
843  107  943  649  806  730  351  102  549  624
955  841  377  309  440  324  539  83   542  116
170  479  963  706  282  962  996  828  392  903
293  422  719  448  772  870  668  36   704  323
674  142  254  548  663  38   724  530  317  191
289  41   265  447  891  371  7    394  630  85
757  967  932  945  627  538  119  930  834  640

2000
20
501  725  359  465  146  828  492  943  437  605
154  383  717  896  727  539  913  300  895  812
170  479  963  706  282  962  996  828  392  903
293  422  719  448  772  870  668  36   704  323
*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值