全网最全面的华为OD机试真题汇总,100%原题题库,不需要开会员即可查看全部内容,更多考题请查看真题库。
真题库:https://www.yuque.com/codernav.com/od
题目:天然蓄水库
知识点双指针
时间限制:1s 空间限制:256MB 限定语言:不限
题目描述:
描述:
公元2919年,人类终于发现了一颗宜居星球——X星。现想在X星一片连绵起伏的山脉间建一个天热蓄水库,如何选取水库边界,使蓄水量最大?
要求:
山脉用正整数数组s表示,每个元素代表山脉的高度。
选取山脉上两个点作为蓄水库的边界,则边界内的区域可以蓄水,蓄水量需排除山脉占用的空间
蓄水量的高度为两边界的最小值。
如果出现多个满足条件的边界,应选取距离最近的一组边界。
输出边界下标(从0开始)和最大蓄水量;如果无法蓄水,则返回0,此时不返回边界。
例如,当山脉为s=[3,1,2]时,则选取s[0]和s[2]作为水库边界,则蓄水量为1,此时输出:0 2:1
当山脉s=[3,2,1]时,不存在合理的边界,此时输出:0。
输入描述:
一行正整数,用空格隔开,例如输入
1 2 3
表示s=[1,2,3]
输出描述:
当存在合理的水库边界时,输出左边界、空格、右边界、英文冒号、蓄水量;例如
0 2:1
当不存在合理的书库边界时,输出0;例如
0
补充说明:
数组s满足:
1 <= length(s) <= 10000
0 <= s[i] <= 10000
示例1
输入:
1 9 6 2 5 4 9 3 7
输出:
1 6:19
说明:
经过分析,选取s[1]和s[6]时,水库蓄水量为19(3+7+4+5)
示例2
输入:
1 8 6 2 5 4 8 3 7
输出:
1 6:15
说明:
经过分析,选取s[1]和s[8]时,水库蓄水量为15;同样选取s[1]和s[6]时,水库蓄水量也为15。由于后者下标距离小(为5),故应选取后者。
示例3
输入:
1 2 3
输出:
0
说明:
不存在合理的水库边界。
真机测试用例:
1 1 2 3 0
4 1 8 6 2 5 4 8 3 7 1 6:15
6 1 7 3 10 10 1 3:4
7 4 3 2 0 2 3 1 5:5
8 1 2 2 0 2 1 2 0 2 2 2 8:5
解题思路:
代码实现一:
package com.codernav.demo.hwod.exam; import java.util.Arrays; import java.util.Scanner; /** * @title 天然蓄水库 * @Description 公元2919年,人类终于发现了一颗宜居星球——X星。现想在X星一片连绵起伏的山脉间建一个天热蓄水库,如何选取水库边界,使蓄水量最大? * 要求: * 山脉用正整数数组s表示,每个元素代表山脉的高度。 * 选取山脉上两个点作为蓄水库的边界,则边界内的区域可以蓄水,蓄水量需排除山脉占用的空间 * 蓄水量的高度为两边界的最小值。 * 如果出现多个满足条件的边界,应选取距离最近的一组边界。 * 输出边界下标(从0开始)和最大蓄水量;如果无法蓄水,则返回0,此时不返回边界。 * 例如,当山脉为s=[3,1,2]时,则选取s[0]和s[2]作为水库边界,则蓄水量为1,此时输出:0 2:1 * 当山脉s=[3,2,1]时,不存在合理的边界,此时输出:0。 * @Author 开发者导航 * @website https://codernav.com * @date 2023/5/28 */ public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); String[] strings = sc.nextLine().split(" "); int[] mountains = Arrays.stream(strings).mapToInt(Integer::parseInt).toArray(); int min = Arrays.stream(mountains).min().getAsInt(); //最小山峰 int max = 0; //最大面积 int maxLeft = 0; //左边界 int maxRight = 0; //右边界 for (int i = 0; i < mountains.length - 2; i++) { int left = mountains[i]; if (left == min) { //最小山峰为边界无法构成蓄水池 continue; } for (int j = i + 2; j < mountains.length; j++) { int right = mountains[j]; if (right == min) { //最小山峰为边界无法构成蓄水池 continue; } int height = Math.min(left, right); //蓄水池的高度为边界最小值 int area = 0; //蓄水池面积 for (int k = i + 1; k < j; k++) { int areaOne = height - mountains[k]; //当前山峰的蓄水量 if (areaOne > 0) { //蓄水量大于0则表示可以蓄水 area += areaOne; } } if (area > max) { //此时面积最大 max = area; maxLeft = i; maxRight = j; } else if (area == max && (maxRight - maxLeft) > (j - i)) { //面积相等距离较近 maxLeft = i; maxRight = j; } } } System.out.println(max == 0 ? 0 : maxLeft + " " + maxRight + ":" + max); } }
代码实现二:满分Java代码实现
package com.codernav.demo.hwod.exam; import java.util.Arrays; import java.util.HashSet; import java.util.Scanner; /** * @title 天然蓄水库 * @Description 公元2919年,人类终于发现了一颗宜居星球——X星。现想在X星一片连绵起伏的山脉间建一个天热蓄水库,如何选取水库边界,使蓄水量最大? * 要求: * 山脉用正整数数组s表示,每个元素代表山脉的高度。 * 选取山脉上两个点作为蓄水库的边界,则边界内的区域可以蓄水,蓄水量需排除山脉占用的空间 * 蓄水量的高度为两边界的最小值。 * 如果出现多个满足条件的边界,应选取距离最近的一组边界。 * 输出边界下标(从0开始)和最大蓄水量;如果无法蓄水,则返回0,此时不返回边界。 * 例如,当山脉为s=[3,1,2]时,则选取s[0]和s[2]作为水库边界,则蓄水量为1,此时输出:0 2:1 * 当山脉s=[3,2,1]时,不存在合理的边界,此时输出:0。 * @Author 开发者导航 * @website https://codernav.com * @date 2023/5/28 */ public class Main { public static void main(String[] args) { Scanner input = new Scanner(System.in); //山脉的高度数组 Integer[] height = Arrays.stream(input.nextLine().split(" ")).map(Integer::parseInt).toArray(Integer[]::new); int n = height.length; //左侧最高山脉数组 int[] left = new int[n]; for (int i = 1; i < n; i++) { //i左侧的最高山脉 left[i] = Math.max(left[i - 1], height[i - 1]); } //右侧最高山脉数组 int[] right = new int[n]; for (int i = n - 2; i >= 0; i--) { //i右侧的最高山脉 right[i] = Math.max(right[i + 1], height[i + 1]); } //最大蓄水高度跟山脉的总高度数组 int[] waterheight = new int[n]; HashSet<Integer> waters_h = new HashSet<>(); for (int i = 1; i < n - 1; i++) { //当前山脉的最大蓄水量 int wat = Math.max(0, Math.min(left[i], right[i]) - height[i]); if (wat != 0) { //最大蓄水量的高度+山脉的高度 waterheight[i] = wat + height[i]; waters_h.add(waterheight[i]); } } int[] result = new int[3]; for (int sig : waters_h) { //左侧第一个满足条件的山脉 int l = 0; while (waterheight[l] < sig || height[l] >= sig) { l++; } //右侧第一个满足条件的山脉 int r = n - 1; while (waterheight[r] < sig || height[r] >= sig) { r--; } //蓄水量求和 int total_wat = 0; for (int i = l; i <= r; i++) { total_wat += Math.max(0, sig - height[i]); } if (total_wat > result[2]) { //蓄水量大于之前的最大蓄水量则重置左右山脉索引和最大蓄水量 result[0] = l - 1; result[1] = r + 1; result[2] = total_wat; } else if (total_wat == result[2]) { //蓄水量等于之前最大蓄水量则判断其山脉距离,选其距离较小的 int d = r - l + 1; int d2 = result[1] - result[0] - 1; if (d < d2) { result[0] = l - 1; result[1] = r + 1; } } } if (result[2] == 0) { System.out.print(0); } else { System.out.print(result[0]); System.out.print(" "); System.out.print(result[1]); System.out.print(":"); System.out.print(result[2]); } } }