算法—动态规划(2)0-1背包问题回顾
0-1背包问题应该算是我本人第一个接触的dp问题,不过当时确实是没弄懂。今天重新回顾了一下,不禁又有了新的收获
文章目录
问题描述
n个物品,重量和价值分别为 和 ,背包总的承重力为W,求能装入的最大价值。
问题分析
相比起一维dp,这里的问题变成了二维的:我要求的是价值,但我又不得不考虑重量的限制。
我们先来考虑一下边界状态:
1)
,那么很明显,直接GG,装不了;
2)
,那么都可以装;
好,这两种边界状态下,那么第一个是0;第二个是求和。
那么正常的转态下呢:
我们设函数
表示:当总重量小于j时(也就是说背包还可以装),从下标为i的商品开始挑选,得到的商品的最大值。
我们都知道,dp问题就是一个在动态中“选or不选”的问题。前面已经提到了,如果单件商品重量大于承重范围,那就直接不选了,跳过下一个;如果符合要求,那就看当前这一物品的选入是否比下一物品的选入更有价值。
即状态转移方程如下:
解释一下该方程:
第一行意为:此时重量是符合的,那我就放进去;
第二行:此时重量符合,承重余量面临不足,那么我就考虑了:是放进去还是不放进去,跳过看下一个?
由于j表示放,那么j-w[i]-v[i]就表示不放。
在此我们还有个问题?我们是否需要全部搜索一遍?其实不必,已经搜索过的,就可以跳过。
那么下面来看一下代码:
代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int maxN=3405;
const int maxW=405;
int dp[maxN][maxW];
int N,W;
int w[maxW],v[maxN];
int rec(int i,int j){
if(dp[i][j]>=0) return dp[i][j];//记忆化搜索,搜过的就变成正数了。初始化为-1是防止特殊情况出现
int ans;
if(i==N) ans=0;//超重,一个都放不了
else if(j<w[i]) ans=rec(i+1,j);//能放,一直放
else ans=max(rec(i+1,j),rec(i+1,j-w[i])+v[i]);//要开始挑了
return dp[i][j]=ans;
}
int main(){
memset(dp,-1,sizeof(dp));
cin>>N>>W;
for(int i=0;i<N;i++)
{
cin>>w[i];
cin>>v[i];
}
int res=rec(0,W);
cout<<res<<endl;
return 0;
}
}
总结
0-1背包问题相比入门级别的dp问题,扩展到了二维,即“目标”是一个维度,而限制条件又是另一个维度的事。因此在考虑问题的同时,要更加注重临界转态。另外,在考虑搜索问题的同时,又要注意减枝问题,可采用对数组统一初始化的思路。