noip2005|noip2005 一维采药---非恰 (01背包)

采药 描述 辰辰是个很有潜能、天资聪颖的孩子,他的梦想是称为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

如果你是辰辰,你能完成这个任务吗?
输入 输入的第一行有两个整数T(1 <= T <= 1000)和M(1 <= M <= 100),T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到100之间(包括1和100)的的整数,分别表示采摘某株草药的时间和这株草药的价值。 输出 输出只包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。 样例输入

70 3 71 100 69 1 1 2

样例输出
3


讲解:通过前面两篇文章的讲解,相信对采药的基本思路应当有一定头绪,我在这里要讲的是另一种更简洁的思路。 我们在前两篇文章中,对于状态的定义都是恰用体积j,最后输出答案是还要枚举一遍,确实显得有点麻烦。为了偷懒,此时自然会想到在输出答案时,能否直接输出一个值,而不用枚举呢?如果要做到这个的话,显然 “恰” 的状态定义就不合适了。 我们重新定义f[j],表示时间为 j 时 所能获得最大价值,时间 j 不一定全部用完,那么最后的答案就是 f[t] ,而不用再枚举了。 请仔细感受一下“恰”与“非恰”定义的不同。 程序演示:
#include #include #define maxn (1000+10) using namespace std; int t,m,f[maxn]; void work() { int i,j,v,w; for(scanf("%d%d",&t,&m),i=1; i<=m; i++) for(scanf("%d%d",&v,&w),j=t; j>=v; j--) f[j]=max(f[j],f[j-v]+w); printf("%d\n",f[t]); }int main() { work(); return 0; }

代码解析:枚举到第i个物品,如果不把第i个物品放入背包,则f[j]的最优值为max(f[j],f[j-1]);如果要把第i个物品放入背包,则f[j]的最优值为f[j-v]+w。但为何上面的状态转移方程中并没有f[j-1]呢?这要谈到最优性问题了。 我们对于f[j]的定义是时间为j 时的最优值,则有:(为了方便展示,这里以二维非恰的f[i][j]表示枚举到第i个物品,时间为j时的最优值) f[i-1][j]>=f[i-1][j-1] f[i-1][j-v]>=(f[i-1][(j-1)-v])
f[i][j]=max(f[i-1][j],f[i-1][j-v]+w); f[i][j-1]=max(f[i-1][j-1],f[i-1][(j-1)-v]+w); ===>f[i][j]>=f[i][j-1] ===>f[j]>=f[j-1] 所以状态转移方程中没有f[j-1]。
由上可知,只要我们保证i-1层的最优值,那么第i层必然也是最优的。初始状态,第0层的最优值全为0,由第0层的最优值可以得到第1层的最优值。。。。。以此类推,最终得到第m层的最优值。

到此为止,对于01背包二维转一维的讲解也结束了。如果你还是存在疑惑,请对比三篇文章中的四份代码好好体会一下,如果代码有问题的话,个人建议最最好是自己出一个小数据,t<=10,m<=5比较合适,然后自己按照我的代码,模拟一遍电脑运行的过程,代码理解也就不是大问题了。 有疑问的话,欢迎大家留言,这也有助于我继续完善背包dp这个专题。

    推荐阅读