Acwing第37周周赛
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/f6e29066b63634b4750448629fff69ff.png#pic_center)
第一题不是太难或者太恶心人的情况下,一般不会写哦。
二、截断数组(中等)(二分、哈希表、双指针)
这个数组有两个拆分点,也可以只有一个,我们可以固定第二个拆分点,去找第一个拆分点,使得s1 = s3,因为题目中说了每个元素都>=1,所以第一个拆分点指针往后走s1的区间和肯定是递增的,所以使得s1 = s3的第一个拆分点指针是唯一的!
第二个点固定了,要找第一个点,而且还是递增的关系,显然第一个做法:二分!这样我们便利后面指针位置需要O(n),二分需要O(logn),总共也就是O(nlogn),满足题意。
import java.util.*;
import java.io.*;
public class Main {
static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
public static void main(String[] args) throws IOException {
String[] input = reader.readLine().trim().split(" ");
int n = Integer.parseInt(input[0]);
input = reader.readLine().trim().split(" ");
// 前缀和数组
long[] preSum = new long[n + 1];
for (int i = 1; i <= n; i++) {
preSum[i] = preSum[i - 1] + Integer.parseInt(input[i - 1]);
}
long ans = 0;
// 遍历尾部指针
for (int i = n; i >= 1; i--) {
long tmp = preSum[n] - preSum[i - 1];
// 二分左边部分的头指针
int left = 1, right = i - 1; // 注意选取下标区间不要重叠
while (left <= right) {
int mid = left + (right -left) / 2;
if (preSum[mid] == tmp) {
ans = Math.max(ans, tmp);
break; // 由于数组和是递增的,找到的答案肯定是唯一
} else if (preSum[mid] > tmp) {
// 如果和大于tmp,说明指针靠后,得往前挪
right = mid - 1;
} else {
left = mid + 1;
}
}
}
writer.write(ans + "");
writer.flush();
}
}
前缀和和二分都是对下标及其敏感的算法,两个合在一起,属于是折磨啊。有单调性,可以用二分,其实还可以用双指针。
其他方法?头指针实则是在找满足等式的数,判断是否存在的问题可以用哈希表,时间复杂度就降到O(n),哈希表还具有可扩展性,因为题目中限制了递增这个条件,但哈希表不需要递增就可以找,只需要判断存在与否即可。
import java.util.*;
import java.io.*;
public class Main {
static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
public static void main(String[] args) throws IOException {
String[] input = reader.readLine().trim().split(" ");
int n = Integer.parseInt(input[0]);
input = reader.readLine().trim().split(" ");
// 前缀和数组
long[] preSum = new long[n + 1];
for (int i = 1; i <= n; i++) {
preSum[i] = preSum[i - 1] + Integer.parseInt(input[i - 1]);
}
long ans = 0;
HashSet<Long> set = new HashSet<>();
for (int i = 1; i <= n; i++) {
// 遍历s3
long s3 = preSum[n] - preSum[i - 1];
if (set.contains(s3)) {
ans = Math.max(ans, s3);
}
set.add(preSum[i]);
}
writer.write(ans + "");
writer.flush();
}
}
三、搭档(困难)(贪心、双指针)
看到这数据量,确实就在往DP想,但结果是错误的。关键是为什么DP不行!(一开始想到的DP方程就跟经典问题:编辑距离一样,提交后发现是错误的,主要是因为有下面这种样例),对于a数组:4 5,b数组5 3,没办法实现交叉选择,也就是没办法4匹配3,5匹配5,这样的话是交叉的。
本题较为正统的做法是匈牙利算法,但是用贪心和双指针也是可以做的,先将男生、女生的魅力值从小到大排序,然后用两个指针从男生、女生的尾部开始遍历,如果能够匹配,两个指针同时向前走,如果boy更大,boy移动,如果girl更大,girl移动。
import java.util.*;
import java.io.*;
public class Main {
static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(System.out));
public static void main(String[] args) throws IOException {
String[] input = reader.readLine().trim().split(" ");
int n = Integer.parseInt(input[0]);
int[] a = new int[n];
input = reader.readLine().trim().split(" ");
for (int i = 0; i < n; i++) {
a[i] = Integer.parseInt(input[i]);
}
input = reader.readLine().trim().split(" ");
int m = Integer.parseInt(input[0]);
input = reader.readLine().trim().split(" ");
int[] b = new int[m];
for (int i = 0; i < m; i++) {
b[i] = Integer.parseInt(input[i]);
}
int ans = 0;
Arrays.sort(a);
Arrays.sort(b);
// n、m作为指针移动
n--;m--;
while (n >= 0 && m >= 0) {
if (Math.abs(a[n] - b[m]) <= 1) {
n--;m--; // 都往后移
ans++;
} else if (a[n] > b[m] - 1) { // boy大了
n--;
} else if (a[n] < b[m] + 1) { // girl小了
m--;
}
}
writer.write(ans + "");
writer.flush();
}
}