题目链接:202109-2 非零段划分
时隔几年,又参加了一次CSP认证考试,发现题目属实变难了点。这次是用Java做的,第二题考试时使用的暴力方法,没想出怎么优化,当时只通过了70%。考试后又花时间想了想,在原版本上进行了优化,可以全A了。这里给出优化后的Java版本代码,希望对看到的朋友能起到点儿参考意义。
我想到的用于优化的主要的两个点是:
1. 从小到大遍历数组中每一种非零的值,并将其在每个位置都置零时,如果这个位置的数左右两边都是0,那么其置零后非零段个数-1;如果其左右两边都不是0,那么其置零后非零段个数+1;其他情况非零段个数不变。
2. 用一个TreeMap存储数组中每一种非零的值以及其对应在数组中的下标位置(TreeMap会根据key的大小自动地从小到大排好序),这样在从小到大遍历数组中每一种非零的值并进行置零操作和统计非零段数时就效率很高啦。
Java代码:
import java.util.*;
public class Main {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] A = new int[n+2];//为了方便之后的计算,在原数组前后都补一个零,这样不会影响非零段个数的计算
A[0] = A[n+1] = 0;
Map<Integer, ArrayList<Integer>> map = new TreeMap<>();//在map中存储数组中每一种非零的值以及其对应在数组中的下标位置
for(int i = 1;i <= n;i++){
A[i] = sc.nextInt();
if (A[i] != 0){
if (map.containsKey(A[i])){
map.get(A[i]).add(i);
} else {
ArrayList<Integer> list = new ArrayList<>();
list.add(i);
map.put(A[i],list);
}
}
}
int count = notZero(A);
int max = count;
Collection<ArrayList<Integer>> lists = map.values();
for (ArrayList<Integer> list : lists) { //从小到大遍历数组中每一种非零的值,并将其在每个位置都置零,再计算当前数组的非零段个数
for (int i: list){
A[i] = 0;
if (A[i-1] == 0 && A[i+1] == 0)
count--;
if (A[i-1] != 0 && A[i+1] != 0)
count++;
}
max = Math.max(max, count);
}
System.out.println(max);
}
//计算给定数组里的非零段个数
public static int notZero(int[] arr){
int nums = 0;
for(int i = 0;i < arr.length;i++){
if(arr[i] != 0){
nums++;
for(int j = i+1;j < arr.length;j++){
i = j;
if(arr[j] != 0)
continue;
else
break;
}
}
}
return nums;
}
}