0——1背包
0-1背包:建立一个二维dp[num+1][size+1],dp[i][j]指前i件当容量为j时的最佳状况即能装的最多,状态转移方程:
j<weight[i],dp[i][j]=dp[i-1][j]
j>=weight[i]&&j<=size,dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]+value[i]),
解释:不能装下当前物件时,最好的状态就是上一个的状态,而当能装下时,又要考虑要不要装,如果不装,同上,如果装,就是在前一个状态的基础上进行计算
#include<iostream>
#include<cstring>
using namespace std;
int main(){
int num,w; cin>>num>>w;
int weight[num+1],value[num+1];
for(int i=1;i<=num;i++) cin>>weight[i];
for(int i=1;i<=num;i++) cin>>value[i];
int dp[num+1][w+1];
memset(dp,0,sizeof(dp));
for(int i=1;i<=num;i++)
for(int j=0;j<=w;j++)
if(j>=weight[i]) dp[i][j]=max(dp[i-1][j],dp[i-1][j-weight[i]]+value[i]);
else dp[i][j]=dp[i-1][j];
cout<<dp[num][w];
}
如果从后往前转移方程转化为一维数组,自行思考
完全背包
完全背包是想了两种
*1、*转换成0-1背包来看,先看看状态转移方程:
j<weight[i]时,dp[i][j]=dp[i-1][j];
j>=weight[i]&&j<=size时,dp[i][j]=max(dp[i-1][j],dp[i][j-weight[i]]+value[i]);
这里和之前的0-1背包所不同的是,之前的0-1背包在比较时是与i-1阶段的状态做比较,而完全背包是与当前阶段的状态做比较。因为在思考的时候是在当前阶段是否能装下更多的物品,并且与上一层的状态做比较,先放出二维dp的代码
#include<iostream>
#include<cstring>
using namespace std;
int main(){
int num,size;cin>>num>>size;
int weight[num+1],value[num+1];
for(int i=1;i<=num;i++) cin>>weight[i];
for(int i=1;i<=num;i++) cin>>value[i];
int dp[num+1][size+1];
memset(dp,0,sizeof(dp));
for(int i=1;i<=num;i++){
for(int j=0;j<weight[i]&&j<=size;j++) dp[i][j]=dp[i-1][j];
for(int j=weight[i];j<=size;j++) dp[i][j]=max(dp[i-1][j],dp[i][j-weight[i]]+value[i]);
}
cout<<dp[num][size];
}
然后这是简化成一维dp数组的代码
#include<iostream>
#include<cstring>
using namespace std;
int main(){
int num,size;cin>>num>>size;
int weight[num+1],value[num+1];
for(int i=1;i<=num;i++) cin>>weight[i];
for(int i=1;i<=num;i++) cin>>value[i];
int dp[size+1];
memset(dp,0,sizeof(dp));
for(int i=1;i<=num;i++)
for(int j=weight[i];j<=size;j++) dp[j]=max(dp[j],dp[j-weight[i]]+value[i]);
for(int j=0;j<=size;j++)
cout<<dp[j]<<" ";
cout<<endl;
cout<<dp[size];
}
2、
这个是分别考虑kweight[i]时的最优状态
状态转移方程为:dp[i][j]=max(dp[i][j],dp[i][j-kweight[i]+value[i])
因为k有多重循环,所以在比较的时候应该与自己作比较
先给出二维代码
#include<iostream>
#include<cstring>
using namespace std;
int main(){
int num,size;cin>>num>>size;
int weight[num+1],value[num+1];
for(int i=1;i<=num;i++) cin>>weight[i];
for(int i=1;i<=num;i++) cin>>value[i];
int dp[num+1][size+1];
memset(dp,0,sizeof(dp));
for(int i=1;i<=num;i++){
for(int j=0;j<=size;j++){
for(int k=0;k*weight[i]<=j;k++){
dp[i][j]=max(dp[i][j],dp[i-1][j-k*weight[i]]+k*value[i]);
}
}
}
cout<<dp[num][size]<<endl;
}
这是优化的二维数组代码
#include<iostream>
#include<cstring>
using namespace std;
int main(){
int num,size;cin>>num>>size;
int weight[num+1],value[num+1];
for(int i=1;i<=num;i++) cin>>weight[i];
for(int i=1;i<=num;i++) cin>>value[i];
int dp[size+1];
memset(dp,0,sizeof(dp));
for(int i=1;i<=num;i++){
for(int j=0;j<=size;j++){
for(int k=0;k*weight[i]<=j;k++){
dp[j]=max(dp[j],dp[j-k*weight[i]]+k*value[i]);
}
}
}
cout<<dp[size]<<endl;
}
3、多重背包
多重背包就是在完全背包的基础上对k多了重限制而已,原本是for(int k=0;kweigh[i]<=j;k++)变成了for(int k=0;kweight[i]<=j&&k<=num[i];k++)
并且,在num[i]*weight[i]>=j时,这就等同于完全背包了,所以放出代码
#include<iostream>
#include<cstring>
using namespace std;
int main(){
int n,size;cin>>n>>size;
int weight[n+1],value[n+1],num[n+1];
for(int i=1;i<=n;i++) cin>>weight[i];
for(int i=1;i<=n;i++) cin>>value[i];
for(int i=1;i<=n;i++) cin>>num[i];
int dp[n+1][size+1];
memset(dp,0,sizeof(dp));
for(int i=1;i<=n;i++)
if(num[i]*weight[i]>=size){
for(int j=0;j<weight[i]&&j<=size;j++) dp[i][j]=dp[i-1][j];
for(int j=weight[i];j<=size;j++) dp[i][j]=max(dp[i-1][j],dp[i][j-weight[i]]+value[i]);
}else
for(int j=0;j<=size;j++)
for(int k=0;k*weight[i]<=j&&k<=num[i];k++){
dp[i][j]=max(dp[i][j],dp[i-1][j-k*weight[i]]+k*value[i]);
}
cout<<dp[n][size];
}