动态规划——01背包问题
问题内容
给定n种物品和一背包。物品i的重量是wi,其价值为vi,背包的容量为C。问应如何选择装入背包的物品,使得装入背包中物品的总价值最大?
0-1背包问题是一个特殊的整数规划问题。
问题分析
求物品的总价值
-
采用动态规划的思想,维护一个二维数组dp[i][j]
-
该数组表示为当有前i个物品选择且背包容量为j时最优价值
例如,
物品的价值:8,50,12,20,10
物品的重量:3,20,5,10,5
如
dp[1][1]
表示物品的选择范围只有第一个,且背包现在的容量为1,那么最大价值为0如
dp[1][3]
表示物品的选择范围只有第一个,且背包现在的容量为3,那么最大价值为8如
dp[2][20]
表示物品的选择范围有第一和第二个,且背包现在的容量为20,那么最大价值为50 -
原问题的解即为dp[i][j]数组中的最大值即为
dp[n][c]
(n为物品的数量,c为背包的重量) -
最长连续上升子序列可以通过s[i]中的下标以及该元素中的值决定
算法步骤
-
输入原始数组w[n],v[n]分别表示物品的重量和价值,以及需要维护的数组
dp[n][c]
(初始值为零) -
两重循环,分别遍历物品i,和背包的重量j(目的是得到每种重量之下的最优值,并可以为后面的所需进行数值递推)
-
遍历的过程中:
如果当前所选物品的重量大于背包的中量便采取不放入背包,即当前的
dp[i][j]=dp[i-1][j]
如果当前所选物品的重量小于背包的重量便有两种情况
(1) 放入的该物品还没有不放入该物品产生的价值大,那么当前的背包的价值依然为不放入该物品的价值,即
dp[i][j]=dp[i-1][j]
(2) 放入的该物品比放入该物品产生的价值大,那么当前的背包的价值为放入该物品的价值,即
dp[i][j]=dp[i-1][j-w[i]]+v[i]
-
循环结束即可得到一张二维表,
dp[n][c]
便是n中物品选择下,背包重量为c的最优价值
求选择的物品
采用回溯法,在已有的dp
表中回溯源,因为dp[i][j]无非是通过dp[i-1][j]
或者dp[i-1][j-w[j]]
求得
-
如果当前的
dp[i][j]
是通过dp[i-1][j]
求得,意味着并没有选择该物品,便回溯到dp[i-1][j]
的位置 -
如果当前的
dp[i][j]
是通过dp[i-1][j-w[i]],
意味着选择该物品,便回溯到dp[i-1][j-w[i]]
源代码
#include <bits/stdc++.h>
using namespace std;
const int N=6;
int v[N]={0,8,50,12,20,10};
int w[N]={0,3,20,5,10,5};
int c=30;
int dp[6][31];//当有前i个物品选择背包容量为j时最优价值
int vis[6];
void findwhere(int i,int j){
if(i>0) {
if(dp[i][j]==dp[i-1][j]){vis[i]=0;findwhere(i-1,j);}
else{
if(dp[i][j]=dp[i-1][j-w[i]]+v[i]){
vis[i]=1;
findwhere(i-1,j-w[i]);
}
}
}
}
void print_vis(){
for(int i=1;i<N;i++){
cout<<vis[i]<<" ";
}
cout<<endl;
}
int main() {
for(int i=1;i<=N;i++){
for (int j = 1; j <=c; ++j) {
if(j<w[i]){dp[i][j]=dp[i-1][j];}
else{
if(dp[i-1][j]>dp[i-1][j-w[i]]+v[i]){dp[i][j]=dp[i-1][j];}
else{dp[i][j]=dp[i-1][j-w[i]]+v[i];}
}
}
}
cout<<"max="<<dp[N][c]<<endl;
findwhere(N,c);
print_vis();
return 0;
}