背包问题和算法理解

背包问题算法理解

背包问题及背包算法的核心

背包问题就是添加了限定条件求取最值的问题。求解的核心思想就是————状态转移方程:
f[item_idx][weight_cursor] = max{f[item_idx-1][weight_cursor], f[item_idx-1][weight_cursor-weights[item_idx]] + values[item_idx]},

其中f[item_idx][weight_cursor]为前item_idx个物品,在重量为weight_cursor时的最大价值,weights[item_idx]表示第item_idx个物品的重量, values[item_idx]表示第item_idx个物品的价值,所以上述状态转移方程可以理解为,前item_idx个物品在限制最大重量为weight_cursor时的最大价值是“前item_idx-1个物品在限制最大重量为weight_cursor时的最大价值与前item_idx-1个物品在限制最大重量为weight_cursor-weight[item_idx]时的最大价值加上第item_idx个物品的价值的较大者”;只要理解了这个方程,就可以依据不同的限制条件,实现不同的算法。因为限制条件的各异所以才有了各类的背包问题,如:01背包,完全背包,2维背包,多重背包,混合背包,分组背包,有依赖关系的背包等等。本文的目的就是阐明上述背包问题的具体描述及其算法实现的核心。

01背包问题

问题描述:有n件物品,每件物品的体积为v[i],价值为c[i]。现有一个容量为V的背包,问如何选取物品放入背包,使得背包内物品的总价值最大。其中每种物品最多只能取一件。
核心算法:

for(size_t i = 0; i < v.size(); ++i){
	size_t volume = v[i];
	size_t value = c[i];
	for(size_t volume_cursor = V; volume_cursor >= volume; --volume_cursor){
		if(max_values[volume_cursor] < max_values[volume_cursor - volume] + value)
			max_values[volume_cursor] = max_values[volume_cursor - volume] + value;
	}
}

完全背包

问题描述:有n件物品,每件物品的体积为v[i],价值为c[i]。现有一个容量为V的背包,问如何选取物品放入背包,使得背包内物品的总价值最大。其中每种物品可以任取多次。
核心算法:

for(size_t i = 0; i < v.size(); ++i){
	size_t volume = v[i];
	size_t value = c[i];
	for(size_t volume_cursor = volume; volume_cursor <= V; ++volume_cursor){
		if(max_values[volume_cursor] < max_values[volume_cursor - volume] + value)
			max_values[volume_cursor] = max_values[volume_cursor - volume] + value;
	}
}

2维背包

问题描述:有n件物品,每件物品的体积为v[i],重量为w[i],价值为c[i]。现有一个最大容量为V,最大承重为W的背包,问如何选取物品放入背包,使得背包内物品的总价值最大。其中每种物品最多只能取一件。
核心算法:

for(size_t i = 0; i < v.size(); ++i){
	size_t volume = v[i];
	size_t weight = w[i];
	size_t value = c[i];
	for(size_t volume_cursor = V; volume_cursor >= volume; --volume_cursor){
		for(size_t weight_cursor = W; weight_cursor >= weight; --weight_cursor)
			if(max_values[volume_cursor][weight_cursor] < max_values[volume_cursor - volume][weight_cursor - weight] + value)
				max_values[volume_cursor][weight_cursor] = max_values[volume_cursor - volume][weight_cursor - weight] + value;
	}
}

多重背包

问题描述:有N种物品和一个容量为V的背包,第i种物品最多有m[i]件可用,价值为c[i],体积为v[i],求解:选哪些物品放入背包,可以使得这些物品的价值最大,并且体积总和不超过背包容量。
核心算法:

for(size_t i = 0; i < v.size(); ++i){
	size_t volume = v[i];
	size_t value = c[i];
	size_t num = m[i];
	for(size_t num_idx = 0; volume_cursor <= num; ++volume_cursor){
		for(size_t volume_cursor = V; volume_cursor >= volume; --volume_cursor)
			if(max_values[volume_cursor] < max_values[volume_cursor - volume] + value)
				max_values[volume_cursor] = max_values[volume_cursor - volume] + value;
	}
}

混合背包

问题描述:有N种物品和一个容量为V的背包,第i种物品最多有m[i]件可用,其中当m[i] = 0时,表示第i个物品有无限多个,m[i] = x(x >=1)时,表示第i个物品有x个,价值为c[i],体积为v[i],求解:选哪些物品放入背包,可以使得这些物品的价值最大,并且体积总和不超过背包容量。
核心算法:

for(size_t i = 0; i < v.size(); ++i){
	size_t volume = v[i];
	size_t value = c[i];
	size_t num = m[i];
	if(num){  //01背包和多重背包
		for(size_t num_idx = 0; volume_cursor <= num; ++volume_cursor){
			for(size_t volume_cursor = V; volume_cursor >= volume; --volume_cursor)
				if(max_values[volume_cursor] < max_values[volume_cursor - volume] + value)
					max_values[volume_cursor] = max_values[volume_cursor - volume] + value;
		}
	}else{//完全背包
		for(size_t volume_cursor = volume; volume_cursor <= V; ++volume_cursor)
			if(max_values[volume_cursor] < max_values[volume_cursor - volume] + value)
				max_values[volume_cursor] = max_values[volume_cursor - volume] + value;
	}
}

分组背包

问题描述: 有N件物品和一个容量为V的背包。其中把这N件物品依据互斥性(将相互冲突的物品分为同一组)分组。第i组的第j件件物品的体积是v[i][j],价值是c[i][j]。每组中的物品互相冲突,最多只能选一件。求解将哪些物品装入背包可使这些物品的价值总和最大。
核心算法:

for(size_t group_cursor = 0; group_cursor < G; ++group_cursor){
	size_t num = v[group_curosr].size();
	std::vector<size_t> tmp_max_value(V+1, 0);
	for(size_t item_cursor = 0; item_cursor < num; ++item_cursor){
		size_t volume = v[group_cursor][item_cursor];
		size_t value = c[group_cursor][item_cursor];
		for(size_t volume_cursor = V; volume_cursor >= volume; --volume_cursor) 
			if(tmp_max_value[volume_cursor] <  value)
				tmp_max_value[volume_cursor] =  value;
	}
	for(size_t volume_cursor = V; volume_cursor >= volume; --volume_cursor) 
		if(max_value[volume_cursor] < tmp_max_value[volume] + max_value[volume_cursor - volume])
			max_value[volume_cursor] = tmp_max_value[volume] + max_value[volume_cursor - volume];
}

有依赖关系的背包

问题描述: 有N件物品和一个容量为V的背包。其中把这N件物品依据互斥性(将相互冲突的物品分为同一组)分组,第i组的第j件件物品的体积是v[i][j],价值是c[i][j]。另外,每组的第一个元素是主元素,其他元素为副元素,如果要选择该组的某个(某些)元素就必须要先选择该组的主元素,每个元素最多只能选一件。求解将哪些物品装入背包可使这些物品的价值总和最大。
核心算法:

for(size_t group_cursor = 0; group_cursor < G; ++group_cursor){
	size_t num = v[group_curosr].size();
	std::vector<size_t> tmp_max_value(max_value);
	for(size_t item_cursor = 1; item_cursor < num; ++item_cursor){
		size_t volume = v[group_cursor][item_cursor];
		size_t value = c[group_cursor][item_cursor];
		for(size_t volume_cursor = V; volume_cursor >= volume; --volume_cursor) 
			if(tmp_max_value[volume_cursor] < tmp_max_value[volume_cursor - volume] + value)
				tmp_max_value[volume_cursor] = tmp_max_value[volume_cursor - volume] + value;
	}
	size_t main_colume = v[group_cursor][0];
	size_t main_value = c[group_cursor][0];
	for(size_t volume_cursor = V; volume_cursor >= mian_colume ; --volume_cursor) 
		if(max_value[volume_cursor] < tmp_max_value[volume_cursor - main_volume] + main_value )
			max_value[volume_cursor] = tmp_max_value[volume_cursor - main_volume] + main_value ;
}

完整的实现参考

请参考我的github:https://github.com/fineday4/knapsack_problem.git。欢迎留言讨论和错误指正。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值