fjutacm 2347 采药 背包dp

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

如果你是辰辰,你能完成这个任务吗?

Input
输入文件medic.in的第一行有两个整数T(1 <= T <= 1000)和M(1 <= M <= 100),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到100之间(包括1和100)的整数,分别表示采摘某株草药的时间和这株草药的价值。

Output
输出文件medic.out包括一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。

SampleInput
70 3
71 100
69 1
1 2

SampleOutput
3

Hint:对于30%的数据,M <= 10;
对于全部的数据,M <= 100。

这是我背包dp的入门题,所以我决定第一篇背包dp直接写它的题解。我相信一个算法知识点一定要直接落实到实际问题的解决上,才有可能让人真正理解并学会使用,不扯那么多理论,直接分析题目。
所谓背包,就是给定一定容量T,有多个物品,每个物品需要t的容量,并具有v的价值,问怎么取,能在不超背包总容量的情况下,拿到总价值最多的东西。
题目中,背包的容量就是师傅给定的总时间T。而背包dp的算法基础,就是我们开一个基础大小为T的数组dp,然后我们维护这一个数组的每一个位置i,也就是0~T这一段时间内每一个时间点的最优解。也就是说,我们要想办法保证,每一个时间点都保存着,有这么多时间的情况下,我们所能拿到的最高总价值的东西。
比如在时间点i,存的就应该是我们还剩时间i的时候所能得到的最优解。这样,我们一番动态规划后,dp[T]的值代表我们有T的时间的时候所能得到的最优解,即是我们所要求的答案。至于为什么在每个时间点i我们都要维护当下最优解,结合代码会更好理解,我们在注释里继续分析。

#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <stack>
#include <queue>
#include <vector>
#include <math.h>
#include <iostream>
const int MAX=1e6;
using namespace std;
int t[105], v[105], dp[1005];/// dp[]的大小一定要开到总时间那么大

int main()
{
  int T, M;
  cin >> T >> M;
  for (int i=1; i<=M; i++)
    cin >> t[i] >> v[i];/// 花费t[i]能获得相应的价值v[i]
  for (int i=1; i<=M; i++)/// 每一株药草都试一遍,看的是“是否要取”
  {
    for (int j=T; j>=t[i]; j--)/// 从后往前遍历,我们就能保证在不同的时间点,
///同一株药草至多被取了一次,以维持那个时间点的最优选择。
///如果从前往后遍历,稍微模拟一下我们就会知道,有可能出现一株草药被取多次的
///情况,并且连被取了几次都不清楚,而这题每种草药就独一株
      dp[j] = max(dp[j], dp[j-t[i]]+v[i]);/// 这里就是维护最优解,二选一,一个
///是之前循环留下的暂时最优的(或初始的)状态,一个是取当前时间总量j,预留出
///草药i所需时间t[i]时的当前最优解dp[j-t[i]],补上药草i的价值v[i]。然后我们要
///最多的价值,所以在dp[j]和dp[j-t[i]]+v[i]之间max一下取较大,这就完成了时间
///点j的最优解维护
  }

这里我插一组案例和它的整个运行过程,帮助我们更好地模拟背包dp基本的流程。
10 5

1 2
3 2
2 1
5 4
4 2

t: 1 3 2 5 4
v: 2 2 1 4 2
j: 0 1 2 3 4 5 6 7 8 9 10
i=1开始前: 0 0 0 0 0 0 0 0 0 0 0
i=1结束后: 0 2 2 2 2 2 2 2 2 2 2
i=2结束后: 0 2 2 2 4 4 4 4 4 4 4
i=3结束后: 0 2 2 3 4 4 5 5 5 5 5
i=4结束后: 0 2 2 3 4 4 6 6 7 8 8
i=5结束后: 0 2 2 3 4 4 6 6 7 8 8
最优解自然就是8,然后我们代码接上面的

扫描二维码关注公众号,回复: 9903422 查看本文章
/// 因为这一题,每一层状态都只与上一层的状态有关,也就是考虑完一株药草之后
  ///我们就可以直接开始下一株,所以是一维的简单问题,我们就只开了一个一维的
  ///dp数组进行滚动更新。
  /// 真的要写的话,还有另外一种二维dp数组的写法,开dp[药草株数][总时长],从
  ///前开始更新从后开始更新都一样,在dp[k-1][j]和dp[k-1][j-t[i]]+v[i]中选max赋
  ///给dp[k][j]。
  cout << dp[T];/// 最终,“有T时间情况下能拿到的最多的价值”即我们要的最优解。
  return 0;
}

写了这么多,主要还是希望自己以后想捡回来的时候能秒懂。学习笔记,说到底还是写给自己看的,所以写得可能不是很好,尽量理解。

发布了19 篇原创文章 · 获赞 0 · 访问量 504

猜你喜欢

转载自blog.csdn.net/qq_43317133/article/details/99307784