本人由于水平有限,笔记难免有疏漏与不严谨的地方,请大家给予批评指正,谢谢!
对不起各位,我的信息笔记——动态规划之基础(4)------最长上升子序列续集并没有保存,所以这里并没有上传。不过,我会重新上传的,谢谢大家!!
一、0-1背包
有N件物品和一个容量为V的背包。第i件物品的价格(即体积,下同)是w[i],价值是c[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。
主体思路就在0-1中了,我们先不考虑这些。
分析一下:
由于每一种背包只有一个,所以我们无论是从头开始,还是从尾部开始考虑这个问题结果都一样。
那么,我们开始分析这个问题。对于一个背包,我们只有两种选择(0-1):选(1)还是不选(0)。
如果我们选了这一个背包,那么我们的总价值就是我们现有的价值加上这个背包的价值,但同时,我们的空间就会少一个这个背包的空间;如果不选,我们和以前一样。
举个例子来说:现在你的背包容量还剩5,现有的价值为1,这里有一个背包需占3个单位的容量,价值是6。
如果我们选择了这个背包,我们剩余容量为2,我们总价值是7;不选,我们还是和以前一样。
那如果剩余容量小于背包的空间,怎么办?
直接跳过,不去选择这个背包。
好,我们定义状态:
每一种背包只有一个,这就意味着我们必须要有顺序的构建状态,或者说,是把顺序当作状态的一种要素,定义状态。
假设我们定义d(i)为看这个背包及以前的背包所得最优的价值,那么d(i)又等于什么呢?不知道。换句话说,定义的状态不好,导致转移困难。
那么好像我们的状态定义的有问题,缺了某点要素。
我们再回想一下:如果我们选了这一个背包,那么我们的总价值就是我们现有的价值加上这个背包的价值,但同时,我们的空间就会少一个这个背包的空间。
我们的状态中缺少的是剩余容量这一要素。
好,我们定义d(i,j)为第i个背包在容量为j时所装的最优价值。
如果选了这个背包,那么所得价值: d(i-1,j-w(i))+c(i);
如果不选,那么所得价值就是上一个所得的价值: d(i-1,j);
所以,两者较大的一个为我们的最优解:d(i,j)=max{ d(i-1,j),d(i-1,j-w(i))+c(i)};
于是,仿照最优三角路线,我们可以得出递归代码:
#include<cstdio> #include<iostream> using namespace std; int max(int a,int b){return b>a?b:a;} int w[110]= {},c[110]={},dp[110][1010]= {}; int bf(int t,int m) { if(m==0)return 0; if(dp[m][t]>0)return dp[m][t]; return dp[m][t]=w[m]<=t?max(bf(t,m-1),c[m]+bf(t-w[m],m-1)):bf(t,m-1); } int main() { int m,t; cin>>t>>m; for(int i=1; i<=m; i++)cin>>w[i]>>c[i]; cout<<bf(t,m)<<endl; return 0; }
关于递推,我在开始接触的时候始终不明白,为什么一定要像填一个表一样for循环。
为了解决这个疑惑,我们设有n个背包,背包容量是m。
那么,d(n,m)就是我们想要的答案。
d(n,m)依靠着比较d(n-1,m)和d(n-1,m-w(i))+c(i);
而它们两个状态,依赖于更前面得出的状态。
所以得到d(n,m)相对于得到将前面所有的状态。
所以用填表法可以解决。
如果不明白的自己列一个表,看看。
所以递推代码就很好解决了(细节多,要注意)。
#include<iostream> #include<cstdio> using namespace std; int w[100+10]= {},c[100+10]= {},d[100+10][2000]; int max(int a,int b){return a>b?a:b;} int main() { int t,m; cin>>t>>m; for(int i=1; i<=m; i++)cin>>w[i]>>c[i]; for(int i=1; i<=m; i++) for(int j=0; j<=t; j++) { d[i][j]=(i==1?0:d[i-1][j]); if(w[i]<=j)d[i][j]=max(d[i][j],c[i]+d[i-1][j-w[i]]); } cout<<d[m][t]<<endl; return 0; }
就到这里。