小韦老师@NOIP 普及组-2004-花生采摘

小韦老师@NOIP 普及组-2004-花生采摘

题目:

描述

鲁宾逊先生有一只宠物猴,名叫多多。这天,他们两个正沿着乡间小路散步,突然发现路边的告示牌上贴着一张小小的纸条:“欢迎免费品尝我种的花生!――熊字”。

鲁宾逊先生和多多都很开心,因为花生正是他们的最爱。在告示牌背后,路边真的有一块花生田,花生植株整齐地排列成矩形网格(如图1)。有经验的多多一眼就能看出,每棵花生植株下的花生有多少。为了训练多多的算术,鲁宾逊先生说:“你先找出花生最多的植株,去采摘它的花生;然后再找出剩下的植株里花生最多的,去采摘它的花生;依此类推,不过你一定要在我限定的时间内回到路边。”

xxx

我们假定多多在每个单位时间内,可以做下列四件事情中的一件:

  1. 从路边跳到最靠近路边(即第一行)的某棵花生植株;

  2. 从一棵植株跳到前后左右与之相邻的另一棵植株;

  3. 采摘一棵植株下的花生;

  4. 从最靠近路边(即第一行)的某棵花生植株跳回路边。

现在给定一块花生田的大小和花生的分布,请问在限定时间内,多多最多可以采到多少个花生?注意可能只有部分植株下面长有花生,假设这些植株下的花生个数各不相同。

例如在图 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

题解:

破题:

根据题目的描述,每走一步用一个时间单位,采摘花生用一个时间单位。 到达每个点需要做以下的事情:

  1. 判断剩下的时间是否能到达下个点,并且到达下个点后,采摘下个点的花生后,剩余的时间还够回到马路上(因为题目要求一定要回到马路上)。
  2. 花费一个时间单位采摘花生,并且将花生数累加到结果中 另外,据题意,一开始在马路上,行数是 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;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
鲁宾逊先生有一只宠物猴,名叫多多。这天,他们两个正沿着乡间小路散步,突然发现路边的告示牌上贴着一张小小的纸条:“欢迎免费品尝我种的花生!——熊字”。 鲁宾逊先生和多多都很开心,因为花生正是他们的最爱。在告示牌背后,路边真的有一块花生田,花生植株整齐地排列成矩形网格(如图1)。 有经验的多多一眼就能看出,每棵花生植株下的花生有多少。为了训练多多的算术,鲁宾逊先生说:“你先找出花生最多的植株,去采摘它的花生;然后再找出剩下的植株里花生最多的,去采摘它的花生;依此类推,不过你一定要在我限定的时间内回到路边。” 我们假定多多在每个单位时间内,可以做下列四件事情中的一件: 1) 从路边跳到最靠近路边(即第一行)的某棵花生植株; 2) 从一棵植株跳到前后左右与之相邻的另一棵植株; 3) 采摘一棵植株下的花生; 4) 从最靠近路边(即第一行)的某棵花生植株跳回路边。 现在给定一块花生田的大小和花生的分布,请问在限定时间内,多多最多可以采到多少个花生?注意可能只有部分植株下面长有花生,假设这些植株下的花生个数各不相同。 例如在图2所示的花生田里,只有位于(2, 5), (3, 7), (4, 2), (5, 4)的植株下长有花生,个数分别为13, 7, 15, 9。沿着图示的路线,多多在21个单位时间内,最多可以采到37个花生

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值