本文主要是介绍小韦老师@NOIP 普及组-2004-花生采摘,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
小韦老师@NOIP 普及组-2004-花生采摘
题目:
描述
鲁宾逊先生有一只宠物猴,名叫多多。这天,他们两个正沿着乡间小路散步,突然发现路边的告示牌上贴着一张小小的纸条:“欢迎免费品尝我种的花生!――熊字”。
鲁宾逊先生和多多都很开心,因为花生正是他们的最爱。在告示牌背后,路边真的有一块花生田,花生植株整齐地排列成矩形网格(如图1)。有经验的多多一眼就能看出,每棵花生植株下的花生有多少。为了训练多多的算术,鲁宾逊先生说:“你先找出花生最多的植株,去采摘它的花生;然后再找出剩下的植株里花生最多的,去采摘它的花生;依此类推,不过你一定要在我限定的时间内回到路边。”
我们假定多多在每个单位时间内,可以做下列四件事情中的一件:
-
从路边跳到最靠近路边(即第一行)的某棵花生植株;
-
从一棵植株跳到前后左右与之相邻的另一棵植株;
-
采摘一棵植株下的花生;
-
从最靠近路边(即第一行)的某棵花生植株跳回路边。
现在给定一块花生田的大小和花生的分布,请问在限定时间内,多多最多可以采到多少个花生?注意可能只有部分植株下面长有花生,假设这些植株下的花生个数各不相同。
例如在图 2 所示的花生田里,只有位于 (2, 5), (3, 7), (4, 2), (5, 4) 的植株下长有花生,个数分别为 13, 7, 15, 9。沿着图示的路线,多多在 21 个单位时间内,最多可以采到 37 个花生。
输入
第一行包括三个整数,M, N 和 K,用空格隔开;表示花生田的大小为 M × N(1 ≤ M, N ≤ 20),多多采花生的限定时间为 K(0 ≤ K ≤ 1000)个单位时间。接下来的 M 行,每行包括 N 个非负整数,也用空格隔开;第 i + 1 行的第 j 个整数 Pij(0 ≤ Pij ≤ 500) 表示花生田里植株 (i, j) 下花生的数目,0 表示该植株下没有花生。
输出
一个整数,即在限定时间内,多多最多可以采到花生的个数。
输入样例1
6 7 21
0 0 0 0 0 0 0
0 0 0 0 13 0 0
0 0 0 0 0 0 7
0 15 0 0 0 0 0
0 0 0 9 0 0 0
0 0 0 0 0 0 0
输出样例1
37
输入样例2
6 7 20
0 0 0 0 0 0 0
0 0 0 0 13 0 0
0 0 0 0 0 0 7
0 15 0 0 0 0 0
0 0 0 9 0 0 0
0 0 0 0 0 0 0
输出样例2
28
题解:
破题:
根据题目的描述,每走一步用一个时间单位,采摘花生用一个时间单位。 到达每个点需要做以下的事情:
- 判断剩下的时间是否能到达下个点,并且到达下个点后,采摘下个点的花生后,剩余的时间还够回到马路上(因为题目要求一定要回到马路上)。
- 花费一个时间单位采摘花生,并且将花生数累加到结果中 另外,据题意,一开始在马路上,行数是 0,列数和第一个要去的点(花生数最多的点)相同。
思路:
整体思路:
首先将每个点横坐标、纵坐标和花生数存在一个结构体里,然后组成结构体数组,按照花生数从多到少进行排序。一开始在路边,横坐标是 0,纵坐标是花生数最多的那个点的纵坐标,首先去采摘有花生最多的植株。然后依次去采摘花生次多的植株,直到时间不够。
这里的时间不够理解为:
若时间允许能到达下一个点,但是时间将不允许将下个点的花生采摘后,回到马路上。回到马路上的位置应是行数为 0,列数为出发回马路上的点的列数。
注意采摘花生用一个时间单位完成。
具体步骤:
1.定义:
// 因为是记录每一个点的信息,行和列最高都是 20,所以最多有 20 ×20 = 400 个点 const int N = 410; struct peanut {int x; // 横坐标 int y; // 纵坐标 int num; // 点(x, y)的花生数 } pe[N]; // 定义结构体数组,用来存储每个点的信息 int m, n; // m 行 n 列int k; // k 个时间单位,要在 k 个时间单位内回到马路上
2.输入 m,n 和 k。
3.定义一个变量 cnt,作为 pe 数组的下标:
int cnt = 1; // 这是 pe 数组的下标,从 1 开始
4.输入花生田,并将每个点的信息存储到 pe 数组中,注意最后 pe 的下标范围是 1~cnt-1
for (int i = 1; i <= m; i++) { // 输入 m 行 n 列的花生田 for (int j = 1; j <= n; j++) {pe[cnt].x = i; // 将横坐标记下 pe[cnt].y = j; // 将纵坐标记下 cin >> pe[cnt].num; // 将花生数读入 // 下标加 1,注意最后一次加 1 的时候已经不是 pe 的下标了,// 所以最后 pe 数组的下标范围是 1~cnt-1 cnt++; }}
5.用 sort 给 pe 数组排序:
// 用 sort 给 pe 数组排序,下标范围是 1~cnt-1,所以第一个参数是 pe + 1,// 第二个参数是最后一个参加排序的元素的下标加 1,cmp 函数实现排序的规则,见主函数上面 sort(pe + 1, pe + cnt, cmp);
6.定义一个变量 ans,用来存储结果:
int ans = 0; // 累加器,用来记录目前采摘的花生数
7.初始化马路出发点的坐标:
pe[0].x = 0; // 开始在马路上的横坐标,为 0 // 开始在马路上的纵坐标,为最高花生数的点的纵坐标(准备去采摘花生的第一个点) pe[0].y = pe[1].y;
8.枚举每个点,若当前点的时间足够,则采摘当前点的花生,并且将所花时间都减掉(从上一个点到当前点的时间,和采摘花生所用的一个时间单位);若时间不足够,则退出循环。
for (int i = 1; i < cnt; i++) { // 枚举每个点 if (check(pe[i-1], pe[i])) { // 用自定义函数判断,若当下这个点时间足够 ans += pe[i].num; // 则将当下这个点的花生采摘(将花生数累加到累加器中) k--; // 采摘花生用一个时间单位,所以 k-- } else break; // 所当下这个点的时间不够,则退出 for 循环 }
9.输出结果。
10.cmp 函数的实现:
// 用 sort 给结构体数组 pe 排序的比较函数// 返回值是 bool 型,两个参数都是 peanut 类型的,因为 pe 是 peanut 类型的 bool cmp(peanut a, peanut b) {return a.num > b.num; // 可以理解成将花生数多的放在前面 }
11.check 函数的实现:
// 两个参数都是 peanut 型的,参数 a 表示当下所在的点,参数 b 表示即将要去的下一个点 // 函数的功能是判断从 a 点到 b 点是否时间足够,足够则返回 true,否则返回 false bool check(peanut a, peanut b) { int t = abs(b.x - a.x) + abs(b.y - a.y); // 算出从 a 点到 b 点要用多少个时间的单位 k -= t; // 所剩时间减去 t if (k >= b.x + 1) { // 若所剩时间足够在 b 点采摘花生后回到马路上,记得要加 1 return true; // 则返回 true } else return false; // 否则返回 false }
思考:
1°这道题的贪心体现在什么地方?请说明理由
2°如何对这道题进行分解,能够让我们收获最多?
3°这道题给予我们什么样的思考与收获?
完整代码:
#include <bits/stdc++.h>using namespace std;// 因为是记录每一个点的信息,行和列最高都是 20,所以最多有 20 ×20 = 400 个点
const int N = 410;
struct peanut {int x; // 横坐标 int y; // 纵坐标 int num; // 点(x, y)的花生数
} pe[N]; // 定义结构体数组,用来存储每个点的信息
int m, n; // m 行 n 列
int k; // k 个时间单位,要在 k 个时间单位内回到马路上 // 用 sort 给结构体数组 pe 排序的比较函数
// 返回值是 bool 型,两个参数都是 peanut 类型的,因为 pe 是 peanut 类型的
bool cmp(peanut a, peanut b) {return a.num > b.num; // 可以理解成将花生数多的放在前面
}// 两个参数都是 peanut 型的,参数 a 表示当下所在的点,参数 b 表示即将要去的下一个点
// 函数的功能是判断从 a 点到 b 点是否时间足够,足够则返回 true,否则返回 false
bool check(peanut a, peanut b) { int t = abs(b.x - a.x) + abs(b.y - a.y); // 算出从 a 点到 b 点要用多少个时间的单位 k -= t; // 所剩时间减去 t if (k >= b.x + 1) { // 若所剩时间足够在 b 点采摘花生后回到马路上,记得要加 1 return true; // 则返回 true } else return false; // 否则返回 false
}int main() {cin >> m >> n >> k; // 输入行数,列数,限制的时间单位数 int cnt = 1; // 这是 pe 数组的下标,从 1 开始 for (int i = 1; i <= m; i++) { // 输入 m 行 n 列的花生田 for (int j = 1; j <= n; j++) {pe[cnt].x = i; // 将横坐标记下 pe[cnt].y = j; // 将纵坐标记下 cin >> pe[cnt].num; // 将花生数读入 // 下标加 1,注意最后一次加 1 的时候已经不是 pe 的下标了,所以最后 pe 数组的下标范围是 1~cnt-1 cnt++; }}// 用 sort 给 pe 数组排序,下标范围是 1~cnt-1,所以第一个参数是 pe + 1,// 第二个参数是最后一个参加排序的元素的下标加 1,cmp 函数实现排序的规则,见主函数上面 sort(pe + 1, pe + cnt, cmp); int ans = 0; // 累加器,用来记录目前采摘的花生数 pe[0].x = 0; // 开始在马路上的横坐标,为 0 pe[0].y = pe[1].y; // 开始在马路上的纵坐标,为最高花生数的点的纵坐标(准备去采摘花生的第一个点) for (int i = 1; i < cnt; i++) { // 枚举每个点 if (check(pe[i-1], pe[i])) { // 用自定义函数判断,若当下这个点时间足够 ans += pe[i].num; // 则将当下这个点的花生采摘(将花生数累加到累加器中) k--; // 采摘花生用一个时间单位,所以 k-- } else break; // 所当下这个点的时间不够,则退出 for 循环 } cout << ans; // 输出结果 return 0;
}
这篇关于小韦老师@NOIP 普及组-2004-花生采摘的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!