本文已参与「新人创作礼」活动,一起开启掘金创作之路。
0-1背包问题
描述:
一个背包容量为c ,现有n件物品,求能装入背包的最大重量是多少?
输入:
先输入两个正整数n、c(0<n、c<1000),表示物品的件数和背包容量,再输入n个正整数,分别是这n件物品的重量。
输出:
输出能装入背包的最大重量。
输入样例:
5 5023 18 13 35 24
输出样例:
48
思路
方法一:
0-1背包的裸题,那就可以直接写一个01背包的动态转移方程:dp[j]=max(dp[j],dp[j-w[i]]+p[i])。dp[j]的意思是:当背包已装j的重量的物品时的最大价值。那么它可以由背包已装j-w[i]时最大的价值进行转移,即由dp[j-w[i]]+p[i]得到。注意每一次要将dp[]设置为0,因为背包此时无价值。当状态方程枚举结束后,我们再从 dp[]数组中找一遍,求得答案maxx=max{dp[i]}(i from 0 to c),输出答案maxx。这种动态规划的方法的时间复杂度为O(n^2).
ps:0-1背包也可以写成二维dp[][],只是这样写成滚动数组可以更加节省空间。
方法二:
除了直接写0-1背包的动态转移方程,还可以直接写dfs,每一个背包无非就是取和不取两个状态,如果要取则要求背包容量 res>=w[now]。分别用ans1,ans2表示取当前物品,不取当前物品的最大价值,dfs返回max(ans1,ans2),dfs的终止条件是now ==n+1。时间复杂度(2^n)。
ps:方法二相较于方法一思维上更加简单,容易想到,但是代码就相对麻烦,并且时间复杂度不够优秀,当然如果加上记忆化搜索后时间复杂度和动态规划是相当的。我个人更喜欢方法一。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn= 2000+50;
int n,c,w[maxn],dp[maxn],p[maxn];
int main(){
int i,j;
scanf("%d%d",&n,&c);
for(i=1;i<=n;i++)cin>>w[i],p[i]=w[i];
for(i=1;i<=n;i++){
for(j=c;j>=1;j--){
if(j-w[i]>=0&&dp[j]<dp[j-w[i]]+p[i]){
dp[j]=dp[j-w[i]]+p[i];
}
}
}
int maxx=0;
for(i=0;i<=c;i++)
if(maxx<dp[i])
maxx=dp[i];
cout<<maxx<<endl;
return 0;
}
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn= 2000+50;
int n,c,w[maxn],p[maxn];
int dfs(int now,int res){
if(now==n+1)return 0;
int ans1=0,ans2=0;
if(res>=w[now]){
ans1=dfs(now+1,res-w[now])+p[now];
}
ans2=dfs(now+1,res);
if(ans1>=ans2)return ans1;
return ans2;
}
int main(){
int i,j;
scanf("%d%d",&n,&c);
for(i=1;i<=n;i++)cin>>w[i],p[i]=w[i];
cout<<dfs(1,c)<<endl;
return 0;
}