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

来源:互联网 发布:苏联打阿富汗知乎 编辑:程序博客网 时间:2024/05/21 22:39

采药

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

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

讲解:通过前面两篇文章的讲解,相信对采药的基本思路应当有一定头绪,我在这里要讲的是另一种更简洁的思路。
          我们在前两篇文章中,对于状态的定义都是恰用体积j,最后输出答案是还要枚举一遍,确实显得有点麻烦。为了偷懒,此时自然会想到在输出答案时,能否直接输出一个值,而不用枚举呢?如果要做到这个的话,显然 “” 的状态定义就不合适了。
         我们重新定义f[j],表示时间为 j 时 所能获得最大价值,时间 j 不一定全部用完,那么最后的答案就是 f[t] ,而不用再枚举了。
         请仔细感受一下“恰”与“非恰”定义的不同。
程序演示:
#include<cstdio>#include<algorithm>#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这个专题。
0 0