P01
1.题目:
编程题1
P为给定的二维平面整数点集。定义 P 中某点x,如果x满足 P 中任意点都不在 x 的右上方区域内(横纵坐标都大于x),则称其为“最大的”。求出所有“最大的”点的集合。(所有点的横坐标和纵坐标都不重复, 坐标轴范围在[0, 1e9) 内)
如下图:实心点为满足条件的点的集合。请实现代码找到集合 P 中的所有 ”最大“ 点的集合并输出。
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 32M,其他语言64M
输入描述:
第一行输入点集的个数 N, 接下来 N 行,每行两个数字代表点的 X 轴和 Y 轴。 对于 50%的数据, 1 <= N <= 10000; 对于 100%的数据, 1 <= N <= 500000;
输出描述:
输出“最大的” 点集合, 按照 X 轴从小到大的方式输出,每行两个数字分别代表点的 X 轴和 Y轴。
示例1
输入例子:
5 1 2 5 3 4 6 7 5 9 0
输出例子:
4 6 7 5 9 0
2.思路:
首先按照数据量 C语言的1s对应常数级10^8,所以双层for循环O(n^2)可以通过50%的数据。
后来考虑凸包,但是只学到向外围一圈,这个方法需要再学习一下
最后考虑贪心解法,按x或y逆序,此时如果比前面的y或x大即可,画图思考一下即可,若按y逆序直接输出结果即可,但通过率只有60%,按x逆序不借用系统数据结构实现,通过率可达到70%,未想到更好的解法,看到讨论C的用户可以用scanf等方式缩短读取数据时间可过,Java未想到更好的办法。
3.代码
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int N = Integer.parseInt(sc.nextLine());
int[][] star = new int[N][2];
int index = 0;
while(N-- > 0){
int a = sc.nextInt();
int b = sc.nextInt();
star[index][0] = a;
star[index++][1] = b;
}
Arrays.sort(star,(a,b) ->{
return b[0] - a[0];
});
int maxY = -1;
int[] res = new int[500000];
int size = 0;
for(int i = 0;i < star.length;i++){
if(star[i][1] > maxY){
res[size++] = i;
maxY = star[i][1];
}
}
for(int i = size - 1;i >= 0;i--){
System.out.println(star[res[i]][0] + " " + star[res[i]][1]);
}
}
}
P02
1.题目
2.
编程题2
给定一个数组序列, 需要求选出一个区间, 使得该区间是所有区间中经过如下计算的值最大的一个:
区间中的最小数 * 区间所有数的和最后程序输出经过计算后的最大值即可,不需要输出具体的区间。如给定序列 [6 2 1]则根据上述公式, 可得到所有可以选定各个区间的计算值:
[6] = 6 * 6 = 36;
[2] = 2 * 2 = 4;
[1] = 1 * 1 = 1;
[6,2] = 2 * 8 = 16;
[2,1] = 1 * 3 = 3;
[6, 2, 1] = 1 * 9 = 9;
从上述计算可见选定区间 [6] ,计算值为 36, 则程序输出为 36。
区间内的所有数字都在[0, 100]的范围内;
时间限制:C/C++ 3秒,其他语言6秒
空间限制:C/C++ 128M,其他语言256M
输入描述:
第一行输入数组序列长度n,第二行输入数组序列。 对于 50%的数据, 1 <= n <= 10000; 对于 100%的数据, 1 <= n <= 500000;
输出描述:
输出数组经过计算后的最大值。
示例1
输入例子:
3 6 2 1
输出例子:
36
2.思路
1.首先区间最小值与区间和,先想到用线段树,区间最小值和区间和都是O(logn),但是区间和明显使用前缀和计算更快一些,因为不涉及修改操作。但是需要遍历每个区间,时间复杂度为O((n^2)logn)根据数据量来看明显无法通过。
2.因为需要每个区间的最小值和sum,已知所有数都在[0,100],所以能想到和具有单调性,能考虑到贪心解为最大值的平方 和 最小值 * 数组sum取max,但如果有0则无法通过
3.因为具有单调性,只需要确定区间内最小值即可,区间和可以使用前缀和解决,所以想到单调队列+前缀和解决方式,成功通过
3.代码
import java.util.*;
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = Integer.parseInt(sc.nextLine());
String[] s = sc.nextLine().split(" ");
int[] arr = new int[n];
int[] preSum = new int[n + 1];
for (int i = 0; i < n; i++) {
arr[i] = Integer.parseInt(s[i]);
preSum[i + 1] = preSum[i] + arr[i];
}
int ans = 0;
//[j + 1] - [i]
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < n; i++) {
while (!stack.isEmpty() && arr[i] < arr[stack.peek()]) {
int minIndex = stack.pop();
int min = arr[minIndex];
int l = !stack.isEmpty() ? stack.peek() : -1;
l += 1;
int r = i - 1;
ans = Math.max(ans, (preSum[r + 1] - preSum[l]) * min);
}
stack.push(i);
}
while (!stack.isEmpty()) {
int minIndex = stack.pop();
int min = arr[minIndex];
int l = !stack.isEmpty() ? stack.peek() : -1;
l += 1;
int r = n - 1;
ans = Math.max(ans, (preSum[r + 1] - preSum[l]) * min);
}
System.out.println(ans);
}
}