自学内容网 自学内容网

Leetcode - 周赛432

一、3417. 跳过交替单元格的之字形遍历

题目链接
在这里插入图片描述
本题是一道模拟题,第一行走0,2,4,…偶数下标的格子,第二行走1,3,5,…奇数下标的格子(要倒着遍历),可以发现规律:

  • 偶数行从前往后遍历偶数下标的格子
  • 奇数行从后往前遍历奇数下标的格子

代码如下:

class Solution {
    public List<Integer> zigzagTraversal(int[][] g) {
        List<Integer> ans = new ArrayList<>();
        boolean flg = false;
        int n = g.length, m = g[0].length;
        for(int i=0; i<g.length; i++){
            if(!flg){
                for(int j=0; j<m; j+=2){
                    ans.add(g[i][j]);
                }
            }else{
                for(int j=m-1-m%2; j>=0; j-=2){
                    ans.add(g[i][j]);
                }
            }
            flg = !flg;
        }
        return ans;
    }
}

二、3418. 机器人可以获得的最大金币数

题目链接
在这里插入图片描述
定义 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k] : 从 ( 0 , 0 ) (0,0) (0,0) ( i , j ) (i,j) (i,j) 且感化最多 k k k 个强盗可获得的最大金币数量。
选或不选 x = c o i n s [ i ] [ j ] x = coins[i][j] x=coins[i][j]

  • 不选 x x x,它只能从 ( i − 1 , j ) (i-1,j) (i1,j) ( i , j − 1 ) (i,j-1) (i,j1) 转移过来,此时它的 k k k 也不会发生变化,即 f [ i ] [ j ] [ k ] = m a x ( f [ i − 1 ] [ j ] [ k ] , f [ i ] [ j − 1 ] [ k ] ) f[i][j][k]=max(f[i-1][j][k],f[i][j-1][k]) f[i][j][k]=max(f[i1][j][k],f[i][j1][k])
  • 选择 x x x,它只能从 ( i − 1 , j ) (i-1,j) (i1,j) ( i , j − 1 ) (i,j-1) (i,j1) 转移过来,此时它的 k k k 需要分情况讨论:
    • i f ( k > 0 if(k > 0 if(k>0 && x < 0 ) x<0) x<0),此时可以感化当前单元格的强盗,即当前不获得也不损失金币,即 f [ i ] [ j ] [ k ] = m a x ( f [ i ] [ j ] [ k ] , f [ i − 1 ] [ j ] [ k − 1 ] , f [ i ] [ j − 1 ] [ k − 1 ] ) f[i][j][k]=max(f[i][j][k],f[i-1][j][k-1],f[i][j-1][k-1]) f[i][j][k]=max(f[i][j][k],f[i1][j][k1],f[i][j1][k1])
    • 不使用感化能力次数 k k k,直接选 x x x,即 f [ i ] [ j ] [ k ] = m a x ( f [ i ] [ j ] [ k ] , f [ i − 1 ] [ j ] [ k − 1 ] + x , f [ i ] [ j − 1 ] [ k − 1 ] + x ) f[i][j][k]=max(f[i][j][k],f[i-1][j][k-1]+x,f[i][j-1][k-1]+x) f[i][j][k]=max(f[i][j][k],f[i1][j][k1]+x,f[i][j1][k1]+x)

PS:

  • 这里 f [ i ] [ j ] f[i][j] f[i][j]是从 f [ i − 1 ] [ j ] f[i-1][j] f[i1][j] f [ i ] [ j − 1 ] f[i][j-1] f[i][j1]转移过来的,还需要注意越界访问,并且需要预处理第一行和第一列的值,所以在开数组的时候可以多开一行一列,此时递推公式就变成了 f [ i + 1 ] [ j + 1 ] = m a x ( f [ i + 1 ] [ j ] , f [ i ] [ j + 1 ] ) f[i+1][j+1] = max(f[i+1][j],f[i][j+1]) f[i+1][j+1]=max(f[i+1][j],f[i][j+1])。就不需要使用 i f if if 判断是否越界了
  • 该题的答案可以为负值,所以需要在初始化的时候将所有位置置为 − i n f -inf inf,除了 f [ 0 ] [ 1 ] [ k ] f[0][1][k] f[0][1][k] f [ 1 ] [ 0 ] [ k ] f[1][0][k] f[1][0][k] ( k ∈ [ 0 , 2 ] ) (k\in [0,2]) (k[0,2]),因为 f [ 1 ] [ 1 ] [ k ] f[1][1][k] f[1][1][k] ( 0 , 0 ) (0,0) (0,0)需要从上述两个状态转移过来,如果置为 − i n f -inf inf,它在 ( 0 , 0 ) (0,0) (0,0)位置的值就变成了 − i n f -inf inf,这显然不符合题意。

代码如下:

class Solution {
    public int maximumAmount(int[][] coins) {
        int n = coins.length, m = coins[0].length;
        int[][][] f = new int[n+1][m+1][3];
        //不选
        //f[i][j][k] = max(f[i-1][j][k], f[i][j-1][k]);
        //选
        //f[i][j][k] = max(f[i-1][j][k], f[i][j-1][k]) + x
        //if(k > 0 && x < 0)
        //f[i][j][k] = max(f[i-1][j][k-1], f[i][j-1][k-1])
        for(int[][] x : f){
            for(int[] y : x){
                Arrays.fill(y, Integer.MIN_VALUE/2);
            }
        }
        Arrays.fill(f[0][1], 0);
        //此时f[1][1][k]一定会从f[0][1][k]处转移过来,
        //不会出现-inf这种情况,所以两个选一个赋值为0就行
     //f[1][j>=1][k] 和 f[i>=1][1][k] 也是同理
        //Arrays.fill(f[1][0], 0);
        for(int i=0; i<n; i++){
            for(int j=0; j<m; j++){
                int x = coins[i][j];
                for(int k=0; k<3; k++){
                //不选的情况和选的第一种情况合起来
                    f[i+1][j+1][k] = Math.max(f[i+1][j][k], f[i][j+1][k]) + x;
                    if(k > 0 && x < 0)
                        f[i+1][j+1][k] = Math.max(f[i+1][j+1][k], Math.max(f[i+1][j][k-1], f[i][j+1][k-1]));
                }
            }
        }
        return f[n][m][2];
    }
}

三、3419. 图的最大边权的最小值

题目链接
在这里插入图片描述
本题满足三个条件:

  • 所有节点都能到达节点 0,反过来说,从节点 0 可以到所有节点,所以可以反向建图,通过 d f s ( 0 ) dfs(0) dfs(0) 计算可以到达的节点个数。
  • 图中剩余边的最大边权值尽可能小,就是最大化最小值问题,可以使用二分来做。
  • 每个节点至多有 t h r e s h o l d threshold threshold 条出去的边(即出度 ⩽ t h r e s h o l d \leqslant threshold threshold),这是个干扰条件,由于我们建立的是反图,这里的出度实际上是入度,而在 d f s ( 0 ) dfs(0) dfs(0) 遍历的时候,已经保证了所有点的入度为 1,题目也保证了 t h r e s h o l d ⩾ 1 threshold\geqslant1 threshold1,所以只要能 d f s ( 0 ) dfs(0) dfs(0) 遍历到所有节点,这个条件就一定会满足!!!

代码如下:

class Solution {
    public int minMaxWeight(int n, int[][] edges, int t) {
        List<int[]>[] g = new ArrayList[n];
        int mx = 0;
        Arrays.setAll(g, e->new ArrayList<>());
        for(int[] e : edges){
            int x = e[0], y = e[1], w = e[2];
            mx = Math.max(w, mx);
            g[y].add(new int[]{x, w});
        }
        cnt = new int[n];
        int l = 1, r = mx;
        while(l <= r){
            m = (l + r) / 2;
            if(check(g)){
                r = m - 1;
            }else{
                l = m + 1;
            }
        }
        return r + 1 <= mx ? r + 1 : -1;
    }
    //由于每次二分的数字肯定不一样
    //所以可以使用cnt[x] == m 来判断该点是否已经遍历过
    int[] cnt;
    int m;//当前二分的数字
    boolean check(List<int[]>[] g){
        int cnt = dfs(0, g);
        return cnt == g.length;
    }
    int dfs(int x, List<int[]>[] g){
        cnt[x] = m;
        int res = 1;
        for(int[] t : g[x]){
            int y = t[0], w = t[1];
            if(w <= m && cnt[y] != m){
                res += dfs(y, g);
            }
        }
        return res;
    }
}

四、3420. 统计 K 次操作以内得到非递减子数组的数目

题目链接
在这里插入图片描述
本题求有多少满足条件的子数组( k k k + 1 +1 +1 操作内,使其变成非递减的子数组),可以使用滑动窗口,枚举右端点 r r r,维护左端点 l l l,设 c n t cnt cnt 为当前 [ l , r ] [l,r] [l,r] 的数组变成非递减所需的最少操作次数。如果 c n t > k cnt > k cnt>k,需要移动 l l l,直到 c n t ⩽ k cnt\leqslant k cntk,此时以 r r r 为右端点的满足条件的子数组有 r − l + 1 r - l + 1 rl+1

本题的难点在于如何维护 c n t cnt cnt:

  • 进窗口:将子数组变成非递减需要操作 t = m a x ( 0 , m a x ( n u m s [ l : r ] ) − n u m s [ r ] ) t = max(0,max(nums[l:r])-nums[r]) t=max(0,max(nums[l:r])nums[r]) c n t = c n t + t cnt = cnt + t cnt=cnt+t
    • 如何维护区间 [ l , r ] [l,r] [l,r]最大值,可以使用单调队列 que(维护一个单调递减的序列)来维护。
      • 如果 que.size() > 0 && \text{que.size() > 0 \&\&} que.size() > 0 && n u m s [ q u e . p e e k L a s t ( ) ] ⩽ n u m s [ r + 1 ] nums[que.peekLast()] \leqslant nums[r+1] nums[que.peekLast()]nums[r+1] 时,说明 n u m s [ q u e . p e e k L a s t ( ) ] nums[que.peekLast()] nums[que.peekLast()] 不可能是当前区间的最大值了,直接删除 q u e . p o l l L a s t ( ) que.pollLast() que.pollLast(),把 r + 1 r+1 r+1添加进队列 q u e que que 中。
      • 如果 q u e . p e e k F i r s t ( ) < l que.peekFirst() < l que.peekFirst()<l,说明该下标已经不在 [ l , r ] [l,r] [l,r]区间,直接删除 q u e . p o l l F i r s t ( ) que.pollFirst() que.pollFirst()
      • 区间 [ l , r ] [l,r] [l,r]的最大值为 n u m s [ q u e . p e e k F i r s t ( ) ] nums[que.peekFirst()] nums[que.peekFirst()]
  • 出窗口:
    在这里插入图片描述

代码如下:

class Solution {
    public long countNonDecreasingSubarrays(int[] nums, int k) {
        int n = nums.length;
        List<Integer>[] g = new ArrayList[n];
        Arrays.setAll(g, e->new ArrayList<>());
        Deque<Integer> st = new ArrayDeque<>();
        int[] pos = new int[n];//记录右边第一个大于nums[i]的下标
        Arrays.fill(pos, n);
        for(int i=0; i<n; i++){
            int v = nums[i];
            while(st.size() > 0 && nums[st.peek()] <= v){
                pos[st.pop()] = i;
            }
            if(st.size() > 0){
                g[st.peek()].add(i);//构建上述所说的树
            }
            st.push(i);
        }
        int cnt = 0;
        long ans = 0;
        Deque<Integer> que = new ArrayDeque<>();
        for(int l=0,r=0; r<n; r++){
            int v = nums[r];
            while(que.size() > 0 && nums[que.getLast()] <= v){
                que.removeLast();
            }
            que.addLast(r);
            cnt += Math.max(nums[que.getFirst()] - v, 0);
            while(cnt > k){
                int x = nums[l];
                for(int i : g[l]){
                    if(i > r) break;
                    cnt -= (x - nums[i]) * (Math.min(pos[i], r+1)-i);
                }
                l++;
                if(!que.isEmpty() && que.getFirst() < l)
                    que.removeFirst();
            }
            ans += r - l + 1;
        }
        return ans;
    }
}

原文地址:https://blog.csdn.net/m0_74859835/article/details/145228510

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!