疯狂的采药
题目背景
此题为NOIP2005普及组第三题的疯狂版。 此题为纪念LiYuxiang而生。
题目描述
LiYuxiang是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:
"孩子,这个山洞里有一些不同种类的草药,采每一种都需要一些时间,每一种也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。"
如果你是LiYuxiang,你能完成这个任务吗?
此题和原题的不同点:
- 每种草药可以无限制地疯狂采摘。
- 药的种类眼花缭乱,采药时间好长好长啊!师傅等得菊花都谢了!
输入输出格式
输入格式
输入第一行有两个整数T(1 <= T <= 100000)和M(1 <= M <= 10000),用一个空格隔开,T代表总共能够用来采药的时间,M代表山洞里的草药的数目。接下来的M行每行包括两个在1到10000之间(包括1和10000)的整数,分别表示采摘某种草药的时间和这种草药的价值。
输出格式
输出一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。
输入输出样例
输入 #1
70 3
71 100
69 1
1 2
输出 #1
140
说明/提示
对于30%的数据,M <= 1000;
对于全部的数据,M <= 10000,且M*T<10000000(别数了,7个0)。
加油LiYuxiang,第一个AC留给你!
分析
此题是一道裸完全背包。
与01
背包不同的是,完全背包一种东西可以无限制的取用。那么假设我采了(n)遍这次草药,那么这种情况下的状态转移方程应该是dp[i][j] = dp[i - 1][j - n * v[i]] + n * w[i]
。
如果我们取整体的状态转移方程,套个(max)就可以了:
dp[i][j] = max(dp[i][j],dp[i - 1][j - n * v[i]] + n * w[i])
。
不过如果这样,时间复杂度就是(O(MT^T)),此题的数据范围是(M le 10000),(T le 100000),所以你用这种方法能T飞。
那怎么办?
只需要把01
背包中内部循环j
的逆序改为顺序即可。如果你不知道01
背包的一维优化中为什么是逆序,见我写的这篇题解。
然后这个又怎么跟01
背包扯上关系呢?首先01
背包所依赖的子结果为dp[i - 1][j - v[i]]
,因为01
背包只能选和不选两种,因此选的状态转移方程中不能含有当前的第i
件物品。也就是说,缩成一维的时候要保证dp[j - v[i]]
逆序遍历,才能保证遍历dp[j]
的时候dp[j - v[i]]
还是上一层的状态(未更新的状态)。
但是完全背包可不一样,爱选多少选多少,所以我们考虑加选一件第i
种物品的策略,就需要考虑dp[i][j - v[i]]
这种子结果。也就是说,完全背包的二维版本中,状态转移方程把dp[i - 1][j - v[i]]
变为了dp[i][j - v[i]]
。这次缩成一维,遍历到dp[j]
的时候,要要求dp[j - v[i]]
是更新的。这就需要正序遍历啦~
也就是说,就是一个倒序变正序。神奇吧?这就是dp
的魅力。
代码
/*
* @Author: crab-in-the-northeast
* @Date: 2020-03-12 20:54:13
* @Last Modified by: crab-in-the-northeast
* @Last Modified time: 2020-03-13 01:28:22
*/
#include <iostream>
#include <cstdio>
const int maxm = 10005;
const int maxt = 100005;
inline int max(int a,int b) {
return a > b ? a : b;
}
int v[maxm],w[maxm],dp[maxt];
int main() {
int T,M;
std :: cin >> T >> M;
for(int i = 1; i <= M; i++)
std :: cin >> v[i] >> w[i];
for(int i = 1; i <= M; i++)
for(int j = v[i]; j <= T; j++)
dp[j] = max(dp[j],dp[j - v[i]] + w[i]);
std :: cout << dp[T] << std :: endl;
return 0;
}
评测结果
RE 44
:R31673389(原因:看错数据范围……弄混数组大小……)
AC 100
:R31673423(这个代码虽然AC了但是关于数组大小方面有点小问题)
AC 100
:R31680243