0-1背包问题
背包问题(Knapsack Problem)
问题描述:一个旅行者准备随身携带一个背包,可以放入背包的物品有nnn种,物品jjj的重量和价值分别为wj,vj,j=1,2,…,nw_j,v_j,j=1,2,…,nwj,vj,j=1,2,…,n,如果背包的最大重量限制是bbb,怎样选择放入背包物品以使得背包的价值最大?
这是一个组合优化问题,设xjx_jxj表示装入背包的第jjj种物品的数量,那么目标函数与约束条件是:
目标函数 max∑j=1nvjxj约束条件 {∑j=1nwjxj≤bxj∈N
\text{目标函数}\space max\sum_{j=1}^nv_jx_j\\
\text{约束条件}\space \begin{cases}
\sum_{j=1}^nw_jx_j\le b\\
x_j\in \text{N}
\end{cases}
目标函数 maxj=1∑nvjxj约束条件 {∑j=1nwjxj≤bxj∈N
当xj=0或1x_j=0或1xj=0或1时的背包问题称之为0-1背包问题。
下面我们讨论其解法。
这个问题是否能由转成子问题的解的组合呢?首先我们可以知道已知条件:nnn个已知重量与其价值的物品,以及背包的最大容量bbb。假设最大价值为FFF,对于一个物品而言,它要么装进背包,要么不装进背包。于是我们可以得到两种情况一种是一个物品装进背包以及其不装进,比较其大小就得到了最大价值。当然如果背包的容量已经装不下该物品,则可以认为最优解是不装该物品。
我们假设这个物品是最后一个物品。于是得到了以下的递推形式:
F(n,b)={max{F(n−1,b),F(n−1,b−wn)+vn} b≥wnF(n−1,b)
F(n,b)=\begin{cases}max\{F(n-1,b),F(n-1,b-w_n)+v_n\} \space b\ge w_n\\
F(n-1,b)
\end{cases}
F(n,b)={max{F(n−1,b),F(n−1,b−wn)+vn} b≥wnF(n−1,b)
可知这个问题满足最优子结构的特征。可以使用动态规划求解。为了保存最大价值我们用一个二维数组来保存对应的F(n,b)F(n,b)F(n,b),为了构造最优解我们通过一个二维数组来保存每次的比较结果S(n,b)S(n,b)S(n,b),其定义如下:
S(n,b)={1 b≥wn且F(n−1,b)≤F(n−1,b−wn)+vn0 其他
S(n,b) = \begin{cases}
1\space\space\space\space b\ge w_n且F(n-1,b)\le F(n-1,b-w_n)+v_n\\
0\space\space\space\space 其他
\end{cases}
S(n,b)={1 b≥wn且F(n−1,b)≤F(n−1,b−wn)+vn0 其他
我们通过S(n,b)S(n,b)S(n,b)就能构造出最优解。
具体伪代码如下:
MakeBest(S,n,b,w)
ans[1……n] = {0,……,0}//最优解
while(b>=0&&n>=0)
ans[n] = S(n,b)
b-=ans[n]*W[n]
n--;
return ans
/**
* 背包问题
* */
#include <iostream>
#include <vector>
using namespace std;
pair<int,vector<int>> KP(vector<int> &w,vector<int> &v,int n,int b){
vector<vector<int>> F(b+1,vector<int>(n+1,0));
vector<vector<int>> S(b+1,vector<int>(n+1,0));
vector<int> ans(n,0);
for (int i = 1; i < b+1; i++)
{
for (int j = 1; j < n+1; j++)
{
if(i-w[j-1]<0) {F[i][j] = F[i][j-1];continue;}
if(F[i][j-1]<F[i-w[j-1]][j-1]+v[j-1]){
F[i][j] = F[i-w[j-1]][j-1]+v[j-1];
S[i][j] = 1;
}
else F[i][j] = F[i][j-1];
}
}
int ANS = F[b][n];
//打印中间过程
for (int i = 1; i < n+1; i++)
{
for (int j = 1; j < b+1; j++)
{
cout<<F[j][i]<<",";
}
cout<<endl;
}
//构造最优解
while(n>=1&&b>=1){
ans[n-1] = S[b][n];
b-=ans[n-1]*w[n-1];
n--;
}
return make_pair(ANS,ans);
}
int main(){
int n = 4,b = 10;
vector<int> w = {2,3,4,7};
vector<int> v = {1,3,5,9};
auto ans = KP(w,v,n,b);
cout<<ans.first<<endl;
for(auto x : ans.second){
cout<<x<<",";
}
system("pause");
}
Reference
《算法设计与分析》第二版 屈婉玲,第三章3.3
本文深入探讨了经典的0-1背包问题,介绍了其数学模型、动态规划解法及具体的实现代码,帮助读者理解如何在有限背包容量下,选择物品以达到价值最大化。
520

被折叠的 条评论
为什么被折叠?



