这应该是最基础的背包问题了,但正因为他的基础,他的思想会被用在很多DP题中,所以这里有必要对他剖析一下。
我们应该掌握的方法有两种
1、二维数组。
2、滚动数组。
后面那个一看就要高级一些,但是确实对动归没有太大的帮助,所以大家一定要把第一种方法学好!第二种方法我不做解释,只会黏贴代码,大家认真脑补一下应该也是可以理解的。
题目链接:
01背包问题
二维数组法:
这里定义背包叫做f[i][j],i代表前i个物品的总重为j,f[i][j]一起表示前i个物品的总共价值,所以我们就可以很明显的写出动态转移方程式:
if(w[i]<=v) f[i][v]=max(f[i-1][v],f[i-1][v-w[i]]+c[i]);
else f[i][v]=f[i-1][v];//important
然后大家应该都懂了,至于有些人问为什么中间看着这么别扭不直接这样写:
if(w[i]<=v) f[i+1][v+w[i]]=max(f[i][v]+c[i],f[i+1][v+w[i]]);
······
这当然是有原因的,是因为害怕一直往后扔会出现重复的情况!!!
下面上代码:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
using namespace std;
int m,n;
int w[35],c[35];
int f[35][205];
int main()
{
scanf("%d%d",&m,&n);
for(int i=1;i<=n;++i)
{
scanf("%d%d",&w[i],&c[i]);
}
for(int i=1;i<=n;++i)
{
for(int v=m;v>0;--v)
{
if(w[i]<=v) f[i][v]=max(f[i-1][v],f[i-1][v-w[i]]+c[i]);
else f[i][v]=f[i-1][v];//important
}
}
printf("%d",f[n][m]);
return 0;
}
P.s:中间加important的地方一定不要漏写了!
滚动数组法:
直接贴代码,大家脑补吧!
#include<cstdio>
#include<cstring>
using namespace std;
int bag[305];
int m,n;
int main()
{
scanf("%d%d",&m,&n);
int a,b;
int limit=0;
for(int i=1;i<=n;++i)
{
scanf("%d%d",&a,&b);
for(int j=limit;j>=0;--j)
{
if(bag[j]!=0||(j==0))
{
if(bag[j+a]<bag[j]+b&&j+a<=m)
{
bag[j+a]=bag[j]+b;
}
if(limit<j+a)
limit=j+a;
}
}
}
int ans=-1;
for(int i=0;i<=m;++i)
{
if(bag[i]>ans)
ans=bag[i];
}
printf("%d",ans);
return 0;
}
谢谢采纳!