JOBDU-OJ 1123 采药

来源:互联网 发布:iface7考勤软件 编辑:程序博客网 时间:2024/05/21 22:31

题目描述:
辰辰是个很有潜能、天资聪颖的孩子,他的梦想是称为世界上最伟大的医师。
为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。
医师把他带到个到处都是草药的山洞里对他说:
“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。
我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是辰辰,你能完成这个任务吗?
输入:
输入的第一行有两个整数T(1 <= T <= 1000)和M(1 <= M <= 100),T代表总共能够用来采药的时间,M代表山洞里的草药的数目。
接下来的M行每行包括两个在1到100之间(包括1和100)的的整数,分别表示采摘某株草药的时间和这株草药的价值。
输出:
可能有多组测试数据,对于每组数据,
输出只包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。
样例输入:
70 3
71 100
69 1
1 2
样例输出:
3
来源:
2008年北京大学图形实验室计算机研究生机试真题


这是我的第一篇博文!加油!

最近在准备研究生考试,为了锻炼自己的编程能力,我在九度OJ刷题。

这是一道经典的0-1背包问题

我们把采药的总时间看作背包的容积s,草药的数目看作物品的数目n,每棵草药的时间和价值看作物品的重量list[i].w和价值list[i].v

设状态dp[i][j]为使用前i个物品(对于某个物品,它的状态只有放或不放)在重量不超过j时能达到的最大价值。那么对于每一个dp[i][j],即有两种决策,放入list[i]或不放。假如放入list[i],则此时的dp[i][j]=dp[i-1][j-list[i].w] + list[i].v (注意到j>=list[i].w), 加入不放,则dp[i][j]=dp[i-1][j],即和使用前i-1个物品所能达到的最大价值相等(因为本次没有放)。

另外,对于 j< list[i].w的情况,它显然不可能由本次放入list[i]得到,它只能由dp[i-1][j]得到了。由于是0-1背包问题,故边界条件为 dp[0][j] = 0 0<=j<=s

故状态转移方程为:

    dp[i][j] = max{ dp[i-1][j], dp[i-1][j-list[i].w] + list[i].v }     dp[0][j] = 0   0<=j<=s

源码:

#include<stdio.h>int max(int a, int b){    return (a>b) ? a : b;}int dp[101][1001];struct {    int v;    int w;}list[101];int main(){    int s, n; //时间 数目    while((scanf("%d%d", &s, &n)) != EOF){        for(int i = 0; i <= n; i++){   //clear            dp[0][i] = 0;        }        for(int i = 1; i <= n; i++){    //scanf            scanf("%d%d", &list[i].w,&list[i].v);        }        for(int i=1; i<=n; i++){            for(int j=s; j>=list[i].w; j--)                dp[i][j] = max(dp[i-1][j], dp[i-1][j-list[i].w] + list[i].v);            for(int j=list[i].w-1; j>=0; j--)                dp[i][j] = dp[i-1][j];        }        printf("%d\n",dp[n][s]);    }    return 0;}

我们注意到,转移方程

    dp[i][j] = max{ dp[i-1][j], dp[i-1][j-list[i].w] + list[i].v }     dp[0][j] = 0   0<=j<=s

对于i的每个状态dp[i][j],它的状态只与dp[i-1][j]与dp[i-1][j-list[i].w]有关,从数组的角度看,我们可以认为该行数据只与上一行有关,即我们可以将二维数组优化成一维数组,优化后的状态方程如下:

    dp[j] = max{ dp[j], dp[j-list[i].w] + list[i].v }     dp[j] = 0  0<=j<=s

值得注意的是,每次我们更新dp[j]时,它的当前状态应当是未更新过的,即二维状态方程的dp[i-1][j],所以我们优化成一维后,必须保证当前的dp[i]是未更新过的,考虑到 j-list[i].w < j, 也就是说,只要我们倒序地更新dp[j],就可以保证更新前的dp[j]是未更新的(其实就是使用前i-1个物品后的状态)。
此时不用考虑j

#include<stdio.h>int max(int a, int b){    return (a>b) ? a : b;}int dp[1001];struct {    int v;    int w;}list[101];int main(){    int s, n; //时间 数目    while((scanf("%d%d", &s, &n)) != EOF){        for(int i = 0; i <= n; i++){   //clear            dp[i] = 0;        }        for(int i = 1; i <= n; i++){    //scanf            scanf("%d%d", &list[i].w,&list[i].v);        }        for(int i=1; i<=n; i++){            for(int j=s; j>=list[i].w; j--) //倒序                dp[j] = max(dp[j], dp[j-list[i].w] + list[i].v);        }        printf("%d\n",dp[n][s]);    }    return 0;}

总结,经典的0-1背包问题的状态转移方程

    dp[j] = max{ dp[j], dp[j-list[i].w] + list[i].v }     dp[j] = 0  0<=j<=s    更新dp[j]时倒序更新

关键的还是抽象问题,构建数学模型的能力

0 0
原创粉丝点击