一.01背包问题
1.1 递归写法
int Dp[MXN][MXN];
//从pos开始选择总重小于wei的物品
int Rec(int pos,int wei)
{
if(Dp[pos][wei]>=0) return Dp[pos][wei];
int res;
if(pos==N) res=0;
else if(wei<W[pos]) res=Rec(pos+1,wei);
else res=max(Rec(pos+1,wei),Rec(pos+1,wei-W[pos])+V[pos]);
return Dp[pos][wei]=res;
}
void Fun()
{
memset(Dp,-1,sizeof(Dp));
printf("%d\n",Rec(0,W));
}
1.2 循环写法
1.2.1 从第i个物品之后挑选重量总和小于j的物品
int Dp[MXN][MXN];
void Fun(){
for(int i=N-1;i>=0;--i)
for(int j=0;j<=W;++j)
{
if(j<W[i])
{
Dp[i][j]=Dp[i+1][j];
}
else
{
Dp[i][j]=max(Dp[i+1][j],Dp[i+1][j-W[i]]+V[i]);
}
}
}
1.2.2 从前i个物品中选出重量不超过j的物品(从两个状态推向一个状态)
void Fun()
{
for(int i=0;i<n;++i)
for(int j=0;j<=W;++j)
if(j<w[i])
dp[i+1][j]=dp[i][j];
else
dp[i+1][j]=max(dp[i][j],dp[i][j-w[i]]+v[i]);
printf("%d\n",dp[n][w]);
}
1.2.3 从前i个物品中选出重量不超过j的物品(从一个状态推向两个状态)
void Fun()
{
for(int i=0;i<n;++i)
for(int j=0;j<=W;++j)
{
Dp[i+1][j]=max(Dp[i+1][j],Dp[i][j]);
if(j+w[i]<=W)
Dp[i+1][j+w[i]]=max(Dp[i+1][j+w[i]],Dp[i][j]+v[i]);
}
printf("%d\n",dp[n][w]);
}
重复利用一个数组
int Fun(){
for(int i=0;i<n;++i)
for(int j=W;j>=w[i];--j)
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
return dp[W];
}
01背包变形,重量的范围很大,价值的范围很小
解法
代码如下
int dp[MXN+1][MXN*MXV+1];
int Fun(){
fill(dp[0],dp[0]+MXN*MXV+1,INF):
dp[0][0]=0;
for(int i=0;i<n;++i)
for(int j=0;j<=MXN*MXV;++j)
if(j<v[i])
dp[i+1][j]=dp[i][j];
else
dp[i+1][j]=min(dp[i][j],dp[i][j-v[i]]+w[i]);
int res=0;
for(int i=0;i<=MXN*MXV;++i)
if(dp[n][i]<=W)
res=i;
return res;
}
二.最长公共子序列(LCS)
void Fun(){
for(int i=0;i<n;++i)
for(int j=0;j<m;++j)
if(s[i]==t[j])
dp[i+1][j+1]=dp[i][j]+1;
else
dp[i+1][j+1]=max(dp[i][j+1],dp[i+1][j]);
return dp[n][m];
}
三.完全背包
3.1 ![](https://img-blog.csdn.net/20131102133811484?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2NjNTI2/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
int Fun(){
for(int i=0;i<n;++i)
for(int j=0;j<=W;++j)
for(int k=0;k*w[i]<=j;++k)
dp[i+1][j]=max(dp[i+1][j],dp[i][j-k*w[i]]+k*v[i]);
return dp[n][W];
}
如图可见
程序如下
int Fun(){
for(int i=0;i<n;++i)
for(int j=0;j<=W;++j)
if(j<w[i])
dp[i+1][j]=dp[i][j];
else
dp[i+1][j]=max(dp[i][j],dp[i+1][j-w[i]]+v[i]);
return dp[n][W];
}
重复利用一个数组
int Fun(){
for(int i=0;i<n;++i)
for(int j=w[i];j<=W;++j)
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
return dp[W];
}
由于dp[i+1]计算只需要dp[i]和dp[i+1],所以结合奇偶性,使用滚动数组
int Dp[2][MXW+1];
int Fun(){
for(int i=0;i<n;++i)
for(int j=0;j<=W;++j)
if(j<w[i])
dp[(i+1)&1][j]=dp[i&1][j];
else
dp[(i+1)&1][j]=max(dp[i&1][j],dp[(i+1)&1][j-w[i]]+v[i]);
return dp[n&1][W];
}