[NOIP2005 普及组] 采药
题目描述
辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是辰辰,你能完成这个任务吗?
输入格式
第一行有 2 2 2 个整数 T T T( 1 ≤ T ≤ 1000 1 \le T \le 1000 1≤T≤1000)和 M M M( 1 ≤ M ≤ 100 1 \le M \le 100 1≤M≤100),用一个空格隔开, T T T 代表总共能够用来采药的时间, M M M 代表山洞里的草药的数目。
接下来的 M M M 行每行包括两个在 1 1 1 到 100 100 100 之间(包括 1 1 1 和 100 100 100)的整数,分别表示采摘某株草药的时间和这株草药的价值。
输出格式
输出在规定的时间内可以采到的草药的最大总价值。
样例 #1
样例输入 #1
70 3
71 100
69 1
1 2
样例输出 #1
3
提示
【数据范围】
- 对于 30 % 30\% 30% 的数据, M ≤ 10 M \le 10 M≤10;
- 对于全部的数据, M ≤ 100 M \le 100 M≤100。
【题目来源】
NOIP 2005 普及组第三题
思路:很基础的一道01背包问题,考虑f[i][j]为第i个物品的情况,那就存在两种情况一个是选第i个物品,另一个是不选,则可以把第i个物品的状态转移给第i-1个物品,以此类推就能得到状态转移方程并且因为每次求的这层只与上一层有关故可以用滚动数组把它优化成一维
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int f[N], v[N], w[N];
int n, m;
int main()
{
cin >> m >> n;
for (int i = 1; i <= n; i++)
{
cin >> v[i] >> w[i];
}
for (int i = 1; i <= n; i++)
{
for (int j = m; j >= v[i]; j--)//为了不让第i层影响前面的值这边选择从后往前遍历
{
f[j] = max(f[j], f[j - v[i]] + w[i]);
}
}
cout << f[m];
return 0;
}
疯狂的采药
题目背景
此题为纪念 LiYuxiang 而生。
题目描述
LiYuxiang 是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同种类的草药,采每一种都需要一些时间,每一种也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”
如果你是 LiYuxiang,你能完成这个任务吗?
此题和原题的不同点:
1 1 1. 每种草药可以无限制地疯狂采摘。
2 2 2. 药的种类眼花缭乱,采药时间好长好长啊!师傅等得菊花都谢了!
输入格式
输入第一行有两个整数,分别代表总共能够用来采药的时间 t t t 和代表山洞里的草药的数目 m m m。
第 2 2 2 到第 ( m + 1 ) (m + 1) (m+1) 行,每行两个整数,第 ( i + 1 ) (i + 1) (i+1) 行的整数 a i , b i a_i, b_i ai,bi 分别表示采摘第 i i i 种草药的时间和该草药的价值。
输出格式
输出一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。
样例 #1
样例输入 #1
70 3
71 100
69 1
1 2
样例输出 #1
140
提示
数据规模与约定
- 对于 30 % 30\% 30% 的数据,保证 m ≤ 1 0 3 m \le 10^3 m≤103 。
- 对于 100 % 100\% 100% 的数据,保证 1 ≤ m ≤ 1 0 4 1 \leq m \le 10^4 1≤m≤104, 1 ≤ t ≤ 1 0 7 1 \leq t \leq 10^7 1≤t≤107,且 1 ≤ m × t ≤ 1 0 7 1 \leq m \times t \leq 10^7 1≤m×t≤107, 1 ≤ a i , b i ≤ 1 0 4 1 \leq a_i, b_i \leq 10^4 1≤ai,bi≤104。
思路:一道完全背包的板子题
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e7 + 10;
long long f[N], v[N], w[N];
int n, m;
int main()
{
cin >> m >> n;
for (int i = 1; i <= n; i++)
{
cin >> v[i] >> w[i];
}
for (int i = 1; i <= n; i++)
{
for (int j = v[i]; j <= m; j++)
{
f[j] = max(f[j], f[j - v[i]] + w[i]);
}
}
cout << f[m];
return 0;
}
[NOIP2001 普及组] 装箱问题
题目描述
有一个箱子容量为 V V V,同时有 n n n 个物品,每个物品有一个体积。
现在从 n n n 个物品中,任取若干个装入箱内(也可以不取),使箱子的剩余空间最小。输出这个最小值。
输入格式
第一行共一个整数 V V V,表示箱子容量。
第二行共一个整数 n n n,表示物品总数。
接下来 n n n 行,每行有一个正整数,表示第 i i i 个物品的体积。
输出格式
- 共一行一个整数,表示箱子最小剩余空间。
样例 #1
样例输入 #1
24
6
8
3
12
7
9
7
样例输出 #1
0
提示
对于 100 % 100\% 100% 数据,满足 0 < n ≤ 30 0<n \le 30 0<n≤30, 1 ≤ V ≤ 20000 1 \le V \le 20000 1≤V≤20000。
【题目来源】
NOIP 2001 普及组第四题
思路:这题乍眼一看,怎么只有体积这一个属性,其实通过一通思考可以发现,能把题意等价为在容量为V的背包里存体积和价值相同的物品的最大值,这样又变成基础的01背包问题了
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 2e4 + 10;
int a[N], f[N];
int n, m;
int main()
{
cin >> m >> n;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
for (int i = 1; i <= n; i++)
{
for(int j = m; j >= a[i]; j--)
f[j] = max(f[j], f[j - a[i]] + a[i]);
}
cout << m - f[m];
return 0;
}
樱花
题目背景
《爱与愁的故事第四弹·plant》第一章。
题目描述
爱与愁大神后院里种了 n n n 棵樱花树,每棵都有美学值 C i ( 0 ≤ C i ≤ 200 ) C_i(0 \le C_i \le 200) Ci(0≤Ci≤200)。爱与愁大神在每天上学前都会来赏花。爱与愁大神可是生物学霸,他懂得如何欣赏樱花:一种樱花树看一遍过,一种樱花树最多看 P i ( 0 ≤ P i ≤ 100 ) P_i(0 \le P_i \le 100) Pi(0≤Pi≤100) 遍,一种樱花树可以看无数遍。但是看每棵樱花树都有一定的时间 T i ( 0 ≤ T i ≤ 100 ) T_i(0 \le T_i \le 100) Ti(0≤Ti≤100)。爱与愁大神离去上学的时间只剩下一小会儿了。求解看哪几棵樱花树能使美学值最高且爱与愁大神能准时(或提早)去上学。
输入格式
共 n + 1 n+1 n+1行:
第
1
1
1 行:现在时间
T
s
T_s
Ts(几时:几分),去上学的时间
T
e
T_e
Te(几时:几分),爱与愁大神院子里有几棵樱花树
n
n
n。这里的
T
s
T_s
Ts,
T
e
T_e
Te 格式为:hh:mm
,其中
0
≤
h
h
≤
23
0 \leq hh \leq 23
0≤hh≤23,
0
≤
m
m
≤
59
0 \leq mm \leq 59
0≤mm≤59,且
h
h
,
m
m
,
n
hh,mm,n
hh,mm,n 均为正整数。
第 2 2 2 行到第 n + 1 n+1 n+1 行,每行三个正整数:看完第 i i i 棵树的耗费时间 T i T_i Ti,第 i i i 棵树的美学值 C i C_i Ci,看第 i i i 棵树的次数 P i P_i Pi( P i = 0 P_i=0 Pi=0 表示无数次, P i P_i Pi 是其他数字表示最多可看的次数 P i P_i Pi)。
输出格式
只有一个整数,表示最大美学值。
样例 #1
样例输入 #1
6:50 7:00 3
2 1 0
3 3 1
4 5 4
样例输出 #1
11
提示
100 % 100\% 100% 数据: T e − T s ≤ 1000 T_e-T_s \leq 1000 Te−Ts≤1000(即开始时间距离结束时间不超过 1000 1000 1000 分钟), n ≤ 10000 n \leq 10000 n≤10000。保证 T e , T s T_e,T_s Te,Ts 为同一天内的时间。
样例解释:赏第一棵樱花树一次,赏第三棵樱花树 2 2 2 次。
思路:题目说当p=0时可以看无数次,其他的p则是有限次,那就可以发现这是完全背包和多重背包的结合,也就是混合背包问题,那么只需要通过p来判断这属于哪种问题,再用相应办法解决即可
代码
#include<bits/stdc++.h>
using namespace std;
int nowh, nowm, goh, gom;
const int N = 1e7+10;
int k[N], t[N], w[N], f[N];
int n, T;
int main()
{
scanf("%d%*c%d", &nowh, &nowm);
scanf("%d%*c%d", &goh, &gom);
T = (goh - nowh) * 60 + gom - nowm;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%d%d%d", &t[i], &w[i], &k[i]);
}
for (int i = 1; i <= n; i++)
{
if (k[i] == 0)
{
for (int j = t[i]; j <= T; j++)
f[j] = max(f[j], f[j - t[i]] + w[i]);
}
else
{
for (int j = T; j >= 0; j--)
{
for (int g = 1; g <= k[i] && g * t[i] <= j; g++)//k从0开始或1都行
f[j] = max(f[j], f[j - g * t[i]] + g * w[i]);
}
}
}
cout << f[T];
return 0;
}