01背包(题目加代码)
题目描述
有 n 件物品和一个容量是 m 的背包。每件物品只能使用一次。
第 i 件物品的体积是 vi,价值是 wi。
求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。
输出最大价值。
输入描述
第一行两个整数,n,m,用空格隔开,分别表示物品数量和背包容积。
接下来有 n 行,每行两个整数 vi,wi,用空格隔开,分别表示第 i 件物品的体积和价值。
数据范围:
0<n,m≤1000
0<vi,wi≤1000
输出描述
输出一个整数,表示最大价值。
样例输入 1
4 5 1 2 2 4 3 4 4 5
样例输出 1
8
一.二维数组
动态规划的核心就是找到原问题与子问题的关系,并列出动态转移方程。
分析:
这里我们可以定义一个二维数组,dp[i][j]表示对于背包容量为j的背包,前i个物品的最优解,即最大价值。
对于一个物品,我们可以分为以下两种情况:
不选:对于dp[i][j],不选第i个物品时,dp[i][j]的最优解就是dp[i-1][j]的最优解
选:如果选择,我们就让背包容量减去第i件的物品体积,让dp加上物品价值,即dp[i][j]=dp[i-1][j-v[i]]+w[i];
这样我们就得到了状态转移方程,如果要计算对于前n个物品背包容量为V的背包的最优解,只需要一层一层往前推,通过前面的子问题,求得最终答案。
状态转移方程:dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);
代码:
#include<bits/stdc++.h>
using namespace std;
int dp[1010][1010];
int v[1010],w[1010];//体积和价值
int main(){
int n,V;
int i,j;
cin>>n>>V;//商品个数和背包容量
for(i=1;i<=n;i++){
cin>>v[i]>>w[i];//体积和价值
}
for(i=1;i<=n;i++){//依次遍历从第1个物品到底n个物品
for(j=1;j<=V;j++){//依次遍历从0~背包容量v
if(j<v[i]){//如果背包容量小于物品体积
dp[i][j]=dp[i-1][j];//最优解就是上一个物品时的最优解
}else{//否则就是背包容量大于等于物品体积
dp[i][j]=max(dp[i-1][j],dp[i-1][j-v[i]]+w[i]);//拿或者不拿,选最优,同时注意是与上一个i-1比较
}
}
}
cout<<dp[n][V]<<endl;//输出前n个商品背包为m的最优解
system("pause");
return 0;
}
二.一维数组优化(防止上面二维时间超限)
从空间优化考虑,在上面的二维数组中我们可以发现有很多重复的数据,我们只要得到第i-1层的结果,就能得到答案,对于前面的数据我们完全没必要储存,所以我们可以定义一个一维数组dp[i]表示背包容量为i时的最优解。我们就可以得到一个状态转移方程:
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
第一个for循环遍历从1~n的物品。第二个for倒序从V到1遍历。这里强调一下,必须是倒序遍历,倒序遍历保证了对于dp[j],他要调用的dp[j-v[i]]一定是第i-1更新过的,第i层还没有进行更新。
好了,话不多说,上代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int dp[N];
int v[N],w[N];//体积和价值
int main(){
int n,V;//商品个数和体积
int i,j;
cin>>n>>V;//商品个数和背包容量
for(i=1;i<=n;i++){
cin>>v[i]>>w[i];//体积和价值
}
for(i=1;i<=n;i++){//依次遍历从第1个物品到底n个物品
for(j=V;j>=v[i];j--){
dp[j]=max(dp[j],dp[j-v[i]]+w[i]);//一维数组从最后往前面遍历
}
}
cout<<dp[V]<<endl;//输出背包为V的最优解
return 0;
}