bp,就是backpack问题,背包问题,也可以称作是knapsack问题。
首先是最简单的背包问题,连续性背包问题,主要用的是贪婪算法(greedy algorithm)。
Problem:
你是一个小偷,有8磅重的背包,你进入了一个人的家里,看到了3kg的金沙,4kg的银子,10kg的葡萄干。你有一个8kg的背包,你应该怎么拿使价值最大。
Answer:
典型的贪婪算法,先3kg金子,再4kg银子,再1kg葡萄干。不给出代码_(:з」∠)_
上面的这个问题,可以说是一个连续的问题,
而接下来的问题,涉及到了动态规划dynamic programming。
动态规划涉及到两个问题:一个是找到重叠子,二是最优子结构。
讨论一下重叠子,其实就是重叠的部分,这里面最典型的例子就是递归。斐波拉契数列用递归计算的话,里面就有很多重叠的地方。假设我们知道第一个和第二个,如果我们要算第n个,那么我们只需要计算n-2次,而递归的机制就很麻烦,不说机制,数据说明一切。
n从3开始到10,次数依次是3 5 9 15 25 41 67 109,当n到30的时候,次数就达到了1664079,这样的计算方式是很耗费时间的,这在ACM这个抢时间的游戏中是不适用的。因为这之中有太多的重叠的地方。这时其实有个很简单的改变,把重复使用的数据“缓存”下来即可。
原递归斐波拉契数列函数:
int fab(int a){
if(a==1 || a==2)
return 1;
else
return (f(a-1)+f(a-2));
}
这种递归的方式速度慢,所以不采纳。
至于更改,采用迭代,从最小的开始就好了。
正式进入正题,动态规划。
当有一个最优子结构,并且局部解决方案有重叠时,就可以考虑采取动规。
现在讨论01背包的问题,就用我们oj上的题目采药1033
先手动的写一张图出来,就用我们sample的数据,这里仔细叙述流程。
1.在山洞里只有5株草药,我们编号1到5,分别对应1到5行,每一株都有各自的采摘时间和价值。
2.从5号草药开始,我们从表格从左往右写,99和100是90,其余的都是0。
3.类推2,写出4号,0到49是0,50到92是46,而99和100,还是90,因为90比较大。
4.类推3,就可以找出一个递推式。
我们设整个的价值表格是c[M][T],M就是草药的棵数,草药一棵棵的来,在设定两个一维数组,t[M]和v[M],分别存储的是草药的采摘时间和价值,这样我们从第一株开始,也就是M=1,M++。每次循环是T=0,T++。
则可以得到表达式:
c[M][T] = max(c[M+1][T], c[M+1][T+t[M]]+v[M])
动态规划的题主要就是找到上面这个表达式,我们取消掉仅做参考用的M,只使用一维数组,下面列出代码:
#include<iostream>
using namespace std;
#define max(a,b) ((a)>(b)?(a):(b))
int main(){
int c[1001] = {0};
int T, M, i, j ,v, n;
cin>>T>>M;
for(i=1; i<=M; i++){
cin>>n>>v;
for(j=T; j>=n; j--)
if(c[j-n] + v > c[j])
c[j] = c[j-n] + v;
}
cout << c[T] << endl;
return 0;
}