背包问题算法理解
背包问题及背包算法的核心
背包问题就是添加了限定条件求取最值的问题。求解的核心思想就是————状态转移方程:
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。欢迎留言讨论和错误指正。