acwing每日一题(8.12 ~ 8.14)

2022.8.12

Leetcode 128  用户分组

1282. 用户分组 - 力扣(LeetCode)

思路:我们用哈希表存储  键:不同大小的组     值:组内元素 (用一个链表或者ArrayList)

如果一个哈希表的组内元素个数  ==  它的组的大小   则说明我们当前组加满了,可以塞进答案中了

遍历所有的点,重复操作即可 (时间复杂度 O(N))

class Solution {
    public List<List<Integer>> groupThePeople(int[] groupSizes) {
        Map<Integer,ArrayList<Integer>> map = new HashMap<>();
        List<List<Integer>> res = new ArrayList<>();
        for (int i = 0; i < groupSizes.length; i ++) {
            int j = groupSizes[i];
            if (map.get(j) == null) {  // 说明还没有ArrayList
                map.put(j,new ArrayList());
            }
            map.get(j).add(i);   // 把元素加入到组内
            if (map.get(j).size() == j) {  // 如果组满了就重开组
                res.add(map.get(j));
                map.remove(j);
            }
        }
        return res;
    }
}

2022.8.13

Leetcode 768    最多能完成排序的块

768. 最多能完成排序的块 II - 力扣(LeetCode) 

思路1:假设我们有一个排序后的组,如果对一段从0开始的区间 它的元素种类和 我们未排序的块一样,那我们可以排序的次数就要加1,因此我们可以用一个hash表来存储,未排序复杂对应元素++,排序复杂元素-- ,如果一段区间元素的个数都是0,则res++(时间复杂度 O(N * logN))

思路二:单调栈的利用,假设我们当前元素i的前边元素都已经分好块了,如果我们 i 小于 上一个块的最大值,则说明上一个块其实没有完全分好(它必须加上我们的i)如果我们的 i 大于上一个元素,则我们的 i 可以成为一个新的块(时间复杂度 O(N))

思路三:很巧妙的利用前缀和的思想,我们可以发现 可以分块的区间它的前缀和都是和排完序之后的区间前缀和相等的(元素一样,和肯定相等)所以我们可以统计有多少次前缀和相等即可(时间复杂度 O(N * Log N))

单调栈代码:

class Solution {
    public int maxChunksToSorted(int[] arr) {
       Deque<Integer> stk = new LinkedList<>();
       for (int x: arr) {
           int t = x;
           while (!stk.isEmpty() && stk.peek() > x) {   // 把不合法的区间合成一段
               t = Math.max(t,stk.peek());
               stk.pop();
           }
           stk.push(t);
       }
       return stk.size();
    }
}

前缀和代码:

class Solution {
    public int maxChunksToSorted(int[] arr) {
        int[] sum = Arrays.copyOf(arr,arr.length);  // 复制加排序
        Arrays.sort(sum);
        for (int i = 0; i < arr.length; i ++) {
            if (i != 0) {     // 前缀和的处理
                sum[i] += sum[i - 1];
                arr[i] += arr[i - 1];
            }
        }
        int res = 0;
        for (int i = 0; i < arr.length; i ++) {
            if (sum[i] == arr[i]) res ++;
        }
        return res;
    }
}

Hash表代码:

class Solution {
    public int maxChunksToSorted(int[] arr) {
        int[] a = Arrays.copyOf(arr,arr.length);
        Arrays.sort(a);
        Map<Integer,Integer> map = new HashMap<>();
        int res = 0;
        for (int i = 0,cnt = 0; i < arr.length; i ++) {
            map.put(arr[i],map.getOrDefault(arr[i],0) + 1);
            if (map.getOrDefault(arr[i],0) == 0) cnt --;
            else if (map.getOrDefault(arr[i],0) == 1)cnt ++;
            map.put(a[i],map.getOrDefault(a[i],0) - 1);
            if (map.getOrDefault(a[i],0) == 0) cnt --;
            else if (map.getOrDefault(a[i],0) == -1) cnt ++;
            if (cnt == 0) res ++;
        }
        return res;
    }
}

2022.8.14

acwing 4405  统计子矩阵个数

c++ 蓝桥杯省赛

4405. 统计子矩阵 - AcWing题库

 

思路:n,m 都是 500 级别 暴力枚举矩阵四个边界时间复杂度是 O(N^4) 肯定超时了

因此考虑双指针优化,固定 一个有边界 r , 左边界 l 定义为最靠左的矩阵之和不超过 k 的边,当 r往右, l 也只能往右 因此得到对于 l 和 r 的单调函数 ,可以采用双指针优化

 预处理前缀和的复杂度是O(N) 的,由于我们固定上下边界,我们只需要处理每一列的前缀和

代码:

import java.util.*;

public class Main{
    static int N = 510;
    static int[][] s = new int[N][N];
    
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(), m = sc.nextInt(), k = sc.nextInt();
        for (int i = 1; i <= n; i ++) {
            for (int j = 1; j <= m; j ++) {
                s[i][j] = sc.nextInt();
                s[i][j] += s[i - 1][j];
            }
        }
        long res = 0;
        for (int i = 1; i <= n; i ++) {
            for (int j = i; j <= n; j ++) {
                for (int l = 1,r = 1,sum = 0; r <= m; r ++) {
                    sum += s[j][r] - s[i - 1][r];  // 加上最右边的一列
                    while (sum > k) {
                        sum -= s[j][l] - s[i - 1][l];  // 总和超过 k 了 需要把最左边的一列剔除
                        l ++;
                    }
                    res += r - l + 1;  // 总和没有超过k 此时l就是对于r 最靠左的一条边界 ,r 可以++
                }
            }
        }
        System.out.print(res);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值