【SOJ】超大背包 (回溯法经典应用)

超大背包

 

前提:01背包问题是NP难问题,解空间可以用子集树表示。

注意点:

1.剪枝条件。一是如果装入后重量超出背包重量就剪枝。二是如果不装入,剩余背包体积可装入价值的上界加上当前价值cv也达不到当前最优价值bestv也剪枝。

2.要求剩余背包可装入体积的上界,需要排序,然后依次装入物品,直到装不下的时候装入一部分。因此可以一开始就对所有物品按照价值重量比进行排序,这样之后的上界计算依次选择即可。

特别注意:

题目的物品体积和价值可以达到10^9,试想极端情况下80个物品的和是会超出int的,因此必须用long long。调了一晚上bug,必须记住!

#include<iostream>
#include<algorithm>
using namespace std;
long long cw;//当前重量
long long cv;//当前价值
long long bestv;//当前最优价值
int n;//物品数
long long v;//背包总重量 
struct Good{
	long long value;
	long long weight;
	bool operator < (const Good &a) const{
		return double(value)/double(weight)>double(a.value)/double(a.weight);
	}
};
Good good[81];
double Bound(int t)//不选择该物品后,如果当前剩余的所有物品装入(完全背包方式)都无法达到当前的bestv,则剪枝 
{
	long long leftw = v - cw;
	double b = cv;//当前价值 
	while(t<=n && good[t].weight<=leftw){
		leftw = leftw - good[t].weight;
		 b += good[t].value;
		 t++;
	 } 
	 if(t<=n) b+= double(leftw)/double(good[t].weight)*double(good[t].value);//t<=n 说明背包不够了,为了计算上界要装入一部分 
	 return b;
}
void dfs(int t)
{
	if(t>n){
		if(cv>bestv) bestv=cv;
		return;
	}
	if(cw+good[t].weight<=v){//如果装入就超过背包重量了剪枝。可以装入,进入左子树。 
		cw += good[t].weight;
		cv += good[t].value;
		dfs(t+1);
		cv -= good[t].value;
		cw -= good[t].weight; 
	}
	if(Bound(t+1)>double(bestv)) //不装入,进入右子树。这里其实不剪枝也能AC 
		dfs(t+1);
}
int main(void)
{
	while(cin>>n>>v){
		cw=cv=bestv=0;
		for(int i=1;i<=n;i++)
			cin>>good[i].weight>>good[i].value; 
		sort(good+1,good+n+1);//排序后顺序选择 
		dfs(1);
		cout<<bestv<<endl;
	}
	return 0;
 } 

可以同时参考的链接:https://blog.csdn.net/qq_24489717/article/details/49950027

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值