双指针:牛的学术圈 I

牛的学术圈 I

www.acwing.com/problem/content/3748/
在这里插入图片描述

  • 计算次数为 10 5 10^5 105 级别,需要使用 O ( n l o g n ) O(nlogn) O(nlogn) 的算法

  • 先将给定的 N N N 个数进行从大到小的排序,判断是否有 h h h 个数满足:

    • 所有数大于等于 h − 1 h-1 h1

      • 因为已经进行排序了,所以只需判断 c h ≥ h − 1 c_h \geq h-1 chh1 是否成立即可
    • 且最多有 L L L 个数等于 h − 1 h-1 h1

      • 暴力算法:直接遍历-> O ( n 2 ) O(n^2) O(n2)
      • 二分 h h h-> O ( n l o g n ) O(nlogn) O(nlogn)
      • 双指针

二分写法 O ( n l o g n ) O(nlogn) O(nlogn)

import java.util.*;

public class Main {
    private static final int N = 100010;
    private static Integer[] c = new Integer[N];

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int L = sc.nextInt();
        for (int i = 1; i <= n; i++) {
            c[i] = sc.nextInt();
        }

        // 从大到小排序
        Arrays.sort(c, 1, n + 1, (a, b) -> {
            // 返回值大于0则交换
            return b - a;
        });

        // 二分法
        int left = 0, right = n;
        while (left < right) {
            int mid = left + right + 1 >> 1;
            if (check(L, mid)) {
                left = mid;
            } else {
                right = mid - 1;
            }
        }
        System.out.println(left);
    }
  
    private static boolean check(int L, int x) {
        for (int i = x; i > 0 && L >= 0; i--) {
            if (c[i] >= x) {
                return true;
            } else if (c[i] == x - 1) {
                L--;
            } else {
                return false;
            }
        }
        return L >= 0;
    }
}

双指针写法 O ( n ) O(n) O(n)

在这里插入图片描述

  • 注意:图中的自变量为 i i i j j j 随着 i i i 的增大而减小,应该是从右下走到左上

  • c[j]​ 是大于等于 i i i 的最后一个数, j j j 为其对应下标

  • c[j]​ 及其左侧所有数都大于等于 i i i,现要求出 c[j]​ 右侧等于 i − 1 i - 1 i1 的数的个数

    • j j j i i i 左侧,说明 j j j i i i 之间存在小于 i i i 的数,由于控制了 c[i] >= i - 1​,所以 j j j i i i 之间的数全部等于 i − 1 i - 1 i1,据此统计个数来更新答案
    • j j j i i i 右侧,说明 i i i 左侧的数也全都大于等于 i i i,此时更新答案
  • 且当某次遍历不更新答案时,则意味着之后也必然不会更新答案了(因为没有足够大的数来满足 h h h

import java.util.*;

public class Main {
    private static final int N = 100010;
    private static Integer[] c = new Integer[N];

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int L = sc.nextInt();
        for (int i = 1; i <= n; i++) {
            c[i] = sc.nextInt();
        }

        // 从大到小排序
        Arrays.sort(c, 1, n + 1, (a, b) -> {
            // 返回值大于0则交换
            return b - a;
        });

        // 双指针
        int res = 0;
        // notes: j是随着i的增大而减小的,和之前同增的情况不一样,j要从n开始
        for (int i = 1, j = n; i <= n; i++) {
            while (j > 0 && c[j] < i) {
                j--;
            }
            if (c[i] >= i - 1 && i - j <= L) {
                res = i;
            } else {
                break;
            }
        }
        System.out.println(res);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值