【算法练习】Acwing第37周周赛


在这里插入图片描述

第一题不是太难或者太恶心人的情况下,一般不会写哦。

二、截断数组(中等)(二分、哈希表、双指针)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这个数组有两个拆分点,也可以只有一个,我们可以固定第二个拆分点,去找第一个拆分点,使得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();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@u@

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值