前言:参考书宪:挑战程序设计竞赛第二版。dp[]一维数组是01背包的最简递推式,关于总重量的倒序问题,以及如何得到递推式,通过最基本的解法,一定的思维,推出01背包----dp的美学
01背包:每件物品只能选一次。
例:n个物品,包的总重量为m。每件物品的重量为w[i],价值为v[i].
4 5
2 3
1 2
3 4
2 2
最朴素的办法,把每件物品依次放入背包试试
int rec(int i,int j){//从第i个物品开始挑选总重小于j的物品
int res;
if(i==n)
res=0;
else if(j<w[i])
res=rec(i+1,j);
else
res=max(rec(i+1,j),rec(i+1,j-w[i])+v[i]);
return res;
}
void solve(){
printf("%d\n",rec(0,m));
}
可以看出rec(3,2)被调用了2次,浪费了计算,应该把结果保留下来,避免再次计算。
int dp[MAX_N+1][MAX_N+1];
int rec(int i,int j) {
if(dp[i][j]>=0)
return dp[i][j];
int res;
if(i==n)
res=0;
else if(j<w[i])
res=rec(i+1,j);
else
res=max(rec(i+1,j),rec(i+1,j-w[i])+v[i]);
return dp[i][j]=res;//俗称记忆化搜素
}
记:dp[i][j]为从第i个物品开始挑选总重不大于j时,总价值的最大值。
int dp[MAX_N+1][MAX_N+1];
void solve(){
for(int i=n-1;i>=0;i--){
for(int j=0;j<=m;j++)//一个状态是由后一个状态推出来的,也符合i=n时,没有剩余物品,res=0的思想
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]);
}
printf("%d\n",dp[0][m]);
}
由上面层层推到的过程,加上一定的思维,得出01背包用dp模拟的终极情况
int dp[MAX_N+1];
void solve(){
for(int i=0;i<n;i++)
for(int j=m;j>=w[i];j--)
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
dp[j-w[i]]为dp[j]的最优子状态