动态规划向来的都是算法问题的难点,在经过一段时间的学习和练习,总结一下一些背包问题的原型。
01背包
01背包可以说是所有背包问题的基础,大致的题意是你有一个容量为v的背包,然后又n种物品,每种物品都有其价值和体积,求出你能拿到最大的物品价值。dp[i][j]表示在拿前面i种物品中,在体积为j的背包,你所能拿到物品的最大价值。这里我们就不难得出状态转移方程,如果这件第i个物品不拿,dp[i][j]=dp[i-1][j],如果第i件物品要拿,dp[i][j]=dp[i-1][j-v[i]]+w[i];
下面是一道例题
输入文件的第一行有两个整数 T和 M,用一个空格隔开,T代表总共能够用来采药的时间,M 代表山洞里的草药的数目。
接下来的 M行每行包括两个在 1 到 100 之间(包括 1 和 1001)的整数,分别表示采摘某株草药的时间和这株草药的价值
#include<iostream>
#include<cstdio>
using namespace std;
const int N=1001;
int p[N],T[N];
int dp[N][N];
dp[i][j]=dp[i-1][j],dp[i][j]=dp[i-1][j-v[i]]+w[i];
int main(){
int t,m;
cin>>t>>m;
for(int i=1;i<=m;i++){
cin>>T[i]>>p[i];
}
for(int i=1;i<=m;i++){
for(int j=1;j<=t;j++){
dp[i][j]=dp[i-1][j];
if(j>=T[i]){
dp[i][j]=max(dp[i][j],dp[i-1][j-T[i]]+p[i]);
}
}
}
cout<<dp[m][t]<<"\n";
return 0;
}
为了方便理解就没有用滚动数组优化空间。如果变成一维的j应该从t开始枚举。
完全背包
完全背包问题就有了不同,完全背包是给你一个体积为v的背包,n种物品,但是每种物品都有无限个可以无限拿,每种物品的价值又有所不同,求出你所能拿到的最大价值。同样的状态表示:dp[i][j];表示前i种物品中所选物品容量不超过j的最大价值。
dp[i][j]=max(dp[i-1][j],dp[i-1][j-1*v]+1*w,dp[i-1][j-2*v]+2*w,……)
dp[i][j-v]=max( dp[i-1][j-v],dp[i-1][j-2*v]+w,dp[i-1][j-3v]+2w……)
通过上面的推导,我们不难发现dp[i][j]=max(dp[i-1][j],dp[i-1][j-v]+w),由此得出状太转移的方程。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e3+3;
int w[N],y[N];
int dp[N][N];
int main(){
int n,v;
cin>>n>>v;
for(int i=1;i<=n;i++)cin>>y[i]>>w[i];
for(int i=1;i<=n;i++){
for(int j=1;j<=v;j++){
dp[i][j]=dp[i-1][j];
if(j>=y[i]){
dp[i][j]=max(dp[i-1][j],dp[i][j-y[i]]+w[i]);//注意
}
}
}
cout<<dp[n][v]<<"\n";
return 0;
}
这里如果要优化成一维数组直接改就行,v直接从1开始枚举,这也是所有背包问题能优化到一维的一个唯一不用将j逆过来的。