【题解】—— LeetCode一周小结27

简介: LeetCode每日一道一周小结27

🌟欢迎来到 我的博客 —— 探索技术的无限可能!

🌟博客的简介(文章目录)


【题解】—— 每日一道题目栏


上接:【题解】—— LeetCode一周小结26

2024.7

1.最大化一张图中的路径价值

题目链接:2065. 最大化一张图中的路径价值

给你一张 无向 图,图中有 n 个节点,节点编号从 0 到 n - 1 (都包括)。同时给你一个下标从 0 开始的整数数组 values ,其中 values[i] 是第 i 个节点的 价值 。同时给你一个下标从 0 开始的二维整数数组 edges ,其中 edges[j] = [uj, vj, timej] 表示节点 uj 和 vj 之间有一条需要 timej 秒才能通过的无向边。最后,给你一个整数 maxTime 。

合法路径 指的是图中任意一条从节点 0 开始,最终回到节点 0 ,且花费的总时间 不超过 maxTime 秒的一条路径。你可以访问一个节点任意次。一条合法路径的 价值 定义为路径中 不同节点 的价值 之和 (每个节点的价值 至多 算入价值总和中一次)。

请你返回一条合法路径的 最大 价值。

注意:每个节点 至多 有 四条 边与之相连。

示例 1:

输入:values = [0,32,10,43], edges = [[0,1,10],[1,2,15],[0,3,10]],

maxTime = 49

输出:75

解释:

一条可能的路径为:0 -> 1 -> 0 -> 3 -> 0 。总花费时间为 10 + 10 + 10 + 10 = 40 <= 49 。

访问过的节点为 0 ,1 和 3 ,最大路径价值为 0 + 32 + 43 = 75 。

示例 2:

输入:values = [5,10,15,20], edges = [[0,1,10],[1,2,10],[0,3,10]],

maxTime = 30

输出:25

解释:

一条可能的路径为:0 -> 3 -> 0 。总花费时间为 10 + 10 = 20 <= 30 。

访问过的节点为 0 和 3 ,最大路径价值为 5 + 20 = 25 。

示例 3:

输入:values = [1,2,3,4], edges = [[0,1,10],[1,2,11],[2,3,12],[1,3,13]],

maxTime = 50

输出:7

解释:

一条可能的路径为:0 -> 1 -> 3 -> 1 -> 0 。总花费时间为 10 + 13 + 13 + 10 = 46 <= 50 。

访问过的节点为 0 ,1 和 3 ,最大路径价值为 1 + 2 + 4 = 7 。

示例 4:

输入:values = [0,1,2], edges = [[1,2,10]], maxTime = 10

输出:0

解释:

唯一一条路径为 0 。总花费时间为 0 。

唯一访问过的节点为 0 ,最大路径价值为 0 。

提示:

n == values.length

1 <= n <= 1000

0 <= values[i] <= 108

0 <= edges.length <= 2000

edges[j].length == 3

0 <= uj < vj <= n - 1

10 <= timej, maxTime <= 100

[uj, vj] 所有节点对 互不相同 。

每个节点 至多有四条 边。

图可能不连通。

题解:

方法:最短路剪枝

       

public class Solution {
    public int maximalPathQuality(int[] values, int[][] edges, int maxTime) {
        int n = values.length;
        List<int[]>[] g = new ArrayList[n];
        Arrays.setAll(g, i -> new ArrayList<>());
        for (int[] e : edges) {
            int x = e[0];
            int y = e[1];
            int t = e[2];
            g[x].add(new int[]{y, t});
            g[y].add(new int[]{x, t});
        }
        // Dijkstra 算法
        int[] dis = new int[n];
        Arrays.fill(dis, Integer.MAX_VALUE);
        dis[0] = 0;
        PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[0] - b[0]);
        pq.add(new int[]{0, 0});
        while (!pq.isEmpty()) {
            int[] p = pq.poll();
            int dx = p[0];
            int x = p[1];
            if (dx > dis[x]) { // x 之前出堆过
                continue;
            }
            for (int[] e : g[x]) {
                int y = e[0];
                int newDis = dx + e[1];
                if (newDis < dis[y]) {
                    dis[y] = newDis; // 更新 x 的邻居的最短路
                    pq.offer(new int[]{newDis, y});
                }
            }
        }
        boolean[] vis = new boolean[n];
        vis[0] = true;
        return dfs(0, 0, values[0], vis, g, values, maxTime, dis);
    }
    private int dfs(int x, int sumTime, int sumValue, boolean[] vis, List<int[]>[] g, int[] values, int maxTime, int[] dis) {
        int res = x == 0 ? sumValue : 0;
        for (int[] e : g[x]) {
            int y = e[0];
            int t = e[1];
            // 相比方法一,这里多了 dis[y]
            if (sumTime + t + dis[y] > maxTime) {
                continue;
            }
            if (vis[y]) {
                res = Math.max(res, dfs(y, sumTime + t, sumValue, vis, g, values, maxTime, dis));
            } else {
                vis[y] = true;
                // 每个节点的价值至多算入价值总和中一次
                res = Math.max(res, dfs(y, sumTime + t, sumValue + values[y], vis, g, values, maxTime, dis));
                vis[y] = false; // 恢复现场
            }
        }
        return res;
    }
}

2.质数的最大距离

题目链接:3115. 质数的最大距离

给你一个整数数组 nums。

返回两个(不一定不同的)质数在 nums 中 下标 的 最大距离。

示例 1:

输入: nums = [4,2,9,5,3]

输出: 3

解释: nums[1]、nums[3] 和 nums[4] 是质数。因此答案是 |4 - 1| = 3。

示例 2:

输入: nums = [4,8,2,8]

输出: 0

解释: nums[2] 是质数。因为只有一个质数,所以答案是 |2 - 2| = 0。

提示:

1 <= nums.length <= 3 * 105

1 <= nums[i] <= 100

输入保证 nums 中至少有一个质数。

题解:

方法:判断质数

       

class Solution {
    public int maximumPrimeDifference(int[] nums) {
        int i = 0;
        while (!isPrime(nums[i])) {
            i++;
        }
        int j = nums.length - 1;
        while (!isPrime(nums[j])) {
            j--;
        }
        return j - i;
    }
    private boolean isPrime(int n) {
        for (int i = 2; i * i <= n; i++) {
            if (n % i == 0) {
                return false;
            }
        }
        return n >= 2;
    }
}

3.哈沙德数

题目链接:3099. 哈沙德数

如果一个整数能够被其各个数位上的数字之和整除,则称之为 哈沙德数(Harshad number)。给你一个整数 x 。如果 x 是 哈沙德数 ,则返回 x 各个数位上的数字之和,否则,返回 -1 。

示例 1:

输入: x = 18

输出: 9

解释:

x 各个数位上的数字之和为 9 。18 能被 9 整除。因此 18 是哈沙德数,答案是 9 。

示例 2:

输入: x = 23

输出: -1

解释:

x 各个数位上的数字之和为 5 。23 不能被 5 整除。因此 23 不是哈沙德数,答案是 -1 。

提示:

1 <= x <= 100

题解:

方法:模拟

       

class Solution {
    public int sumOfTheDigitsOfHarshadNumber(int x) {
        int s = 0;
        for (int y = x; y > 0; y /= 10) {
            s += y % 10;
        }
        return x % s == 0 ? s : -1;
    }
}

4.拾起 K 个 1 需要的最少行动次数

题目链接:3086. 拾起 K 个 1 需要的最少行动次数

给你一个下标从 0 开始的二进制数组 nums,其长度为 n ;另给你一个 正整数 k 以及一个 非负整数 maxChanges 。

Alice 在玩一个游戏,游戏的目标是让 Alice 使用 最少 数量的 行动 次数从 nums 中拾起 k 个 1 。游戏开始时,Alice 可以选择数组 [0, n - 1] 范围内的任何索引 aliceIndex 站立。如果 nums[aliceIndex] == 1 ,Alice 会拾起一个 1 ,并且 nums[aliceIndex] 变成0(这 不算 作一次行动)。之后,Alice 可以执行 任意数量 的 行动(包括零次),在每次行动中 Alice 必须 恰好 执行以下动作之一:

选择任意一个下标 j != aliceIndex 且满足 nums[j] == 0 ,然后将 nums[j] 设置为 1 。这个动作最多可以执行 maxChanges 次。

选择任意两个相邻的下标 x 和 y(|x - y| == 1)且满足 nums[x] == 1, nums[y] == 0 ,然后交换它们的值(将 nums[y] = 1 和 nums[x] = 0)。如果 y == aliceIndex,在这次行动后 Alice 拾起一个 1 ,并且 nums[y] 变成 0 。

返回 Alice 拾起 恰好 k 个 1 所需的 最少 行动次数。

示例 1:

输入:nums = [1,1,0,0,0,1,1,0,0,1], k = 3, maxChanges = 1

输出:3

解释:如果游戏开始时 Alice 在 aliceIndex == 1 的位置上,按照以下步骤执行每个动作,他可以利用 3 次行动拾取 3 个

1 :

游戏开始时 Alice 拾取了一个 1 ,nums[1] 变成了 0。此时 nums 变为 [1,0,1,0,0,1,1,0,0,1] 。

选择 j == 2 并执行第一种类型的动作。nums 变为 [1,0,1,0,0,1,1,0,0,1]

选择 x == 2 和 y == 1 ,并执行第二种类型的动作。nums 变为 [1,1,0,0,0,1,1,0,0,1] 。由于 y ==

aliceIndex,Alice 拾取了一个 1 ,nums 变为 [1,0,0,0,0,1,1,0,0,1] 。

选择 x == 0 和 y == 1 ,并执行第二种类型的动作。nums 变为 [0,1,0,0,0,1,1,0,0,1] 。由于 y ==

aliceIndex,Alice 拾取了一个 1 ,nums 变为 [0,0,0,0,0,1,1,0,0,1] 。

请注意,Alice 也可能执行其他的 3 次行动序列达成拾取 3 个 1 。

示例 2:

输入:nums = [0,0,0,0], k = 2, maxChanges = 3

输出:4

解释:如果游戏开始时 Alice 在 aliceIndex == 0 的位置上,按照以下步骤执行每个动作,他可以利用 4 次行动拾取 2 个

1 :

选择 j == 1 并执行第一种类型的动作。nums 变为 [0,1,0,0] 。

选择 x == 1 和 y == 0 ,并执行第二种类型的动作。nums 变为 [1,0,0,0] 。由于 y ==

aliceIndex,Alice 拾起了一个 1 ,nums 变为 [0,0,0,0] 。

再次选择 j == 1 并执行第一种类型的动作。nums 变为 [0,1,0,0] 。

再次选择 x == 1 和 y == 0 ,并执行第二种类型的动作。nums 变为 [1,0,0,0] 。由于y ==

aliceIndex,Alice 拾起了一个 1 ,nums 变为 [0,0,0,0] 。

提示:

2 <= n <= 105

0 <= nums[i] <= 1

1 <= k <= 105

0 <= maxChanges <= 105

maxChanges + sum(nums) >= k

题解:

方法:贪心 + 前缀和 + 二分查找

       

class Solution {
    public long minimumMoves(int[] nums, int k, int maxChanges) {
        int n = nums.length;
        int[] cnt = new int[n + 1];
        long[] s = new long[n + 1];
        for (int i = 1; i <= n; ++i) {
            cnt[i] = cnt[i - 1] + nums[i - 1];
            s[i] = s[i - 1] + i * nums[i - 1];
        }
        long ans = Long.MAX_VALUE;
        for (int i = 1; i <= n; ++i) {
            long t = 0;
            int need = k - nums[i - 1];
            for (int j = i - 1; j <= i + 1; j += 2) {
                if (need > 0 && 1 <= j && j <= n && nums[j - 1] == 1) {
                    --need;
                    ++t;
                }
            }
            int c = Math.min(need, maxChanges);
            need -= c;
            t += c * 2;
            if (need <= 0) {
                ans = Math.min(ans, t);
                continue;
            }
            int l = 2, r = Math.max(i - 1, n - i);
            while (l <= r) {
                int mid = (l + r) >> 1;
                int l1 = Math.max(1, i - mid), r1 = Math.max(0, i - 2);
                int l2 = Math.min(n + 1, i + 2), r2 = Math.min(n, i + mid);
                int c1 = cnt[r1] - cnt[l1 - 1];
                int c2 = cnt[r2] - cnt[l2 - 1];
                if (c1 + c2 >= need) {
                    long t1 = 1L * c1 * i - (s[r1] - s[l1 - 1]);
                    long t2 = s[r2] - s[l2 - 1] - 1L * c2 * i;
                    ans = Math.min(ans, t + t1 + t2);
                    r = mid - 1;
                } else {
                    l = mid + 1;
                }
            }
        }
        return ans;
    }
}

5.修改矩阵

题目链接:3033. 修改矩阵

给你一个下标从 0 开始、大小为 m x n 的整数矩阵 matrix ,新建一个下标从 0 开始、名为 answer 的矩阵。使 answer 与 matrix 相等,接着将其中每个值为 -1 的元素替换为所在列的 最大 元素。

返回矩阵 answer 。

示例 1:

输入:matrix = [[1,2,-1],[4,-1,6],[7,8,9]]

输出:[[1,2,9],[4,8,6],[7,8,9]]

解释:上图显示了发生替换的元素(蓝色区域)。

  • 将单元格 [1][1] 中的值替换为列 1 中的最大值 8 。
  • 将单元格 [0][2] 中的值替换为列 2 中的最大值 9 。

示例 2:

输入:matrix = [[3,-1],[5,2]]

输出:[[3,2],[5,2]]

解释:上图显示了发生替换的元素(蓝色区域)。

提示:

m == matrix.length

n == matrix[i].length

2 <= m, n <= 50

-1 <= matrix[i][j] <= 100

测试用例中生成的输入满足每列至少包含一个非负整数。

题解:

方法:模拟

       

class Solution {
    public int[][] modifiedMatrix(int[][] matrix) {
        int m = matrix.length, n = matrix[0].length;
        for (int j = 0; j < n; ++j) {
            int mx = -1;
            for (int i = 0; i < m; ++i) {
                mx = Math.max(mx, matrix[i][j]);
            }
            for (int i = 0; i < m; ++i) {
                if (matrix[i][j] == -1) {
                    matrix[i][j] = mx;
                }
            }
        }
        return matrix;
    }
}

6.交替子数组计数

题目链接:3101. 交替子数组计数

给你一个

二进制数组

nums 。

如果一个

子数组

中 不存在 两个 相邻 元素的值 相同 的情况,我们称这样的子数组为 交替子数组 。

返回数组 nums 中交替子数组的数量。

示例 1:

输入: nums = [0,1,1,1]

输出: 5

解释:

以下子数组是交替子数组:[0] 、[1] 、[1] 、[1] 以及 [0,1] 。

示例 2:

输入: nums = [1,0,1,0]

输出: 10

解释:

数组的每个子数组都是交替子数组。可以统计在内的子数组共有 10 个。

提示:

1 <= nums.length <= 105

nums[i] 不是 0 就是 1 。

题解:

方法:枚举

       

class Solution {
    public long countAlternatingSubarrays(int[] nums) {
        long ans = 1, s = 1;
        for (int i = 1; i < nums.length; ++i) {
            s = nums[i] != nums[i - 1] ? s + 1 : 1;
            ans += s;
        }
        return ans;
    }
}

7.检查操作是否合法

题目链接:1958. 检查操作是否合法

给你一个下标从 0 开始的 8 x 8 网格 board ,其中 board[r][c] 表示游戏棋盘上的格子 (r, c) 。棋盘上空格用 ‘.’ 表示,白色格子用 ‘W’ 表示,黑色格子用 ‘B’ 表示。

游戏中每次操作步骤为:选择一个空格子,将它变成你正在执行的颜色(要么白色,要么黑色)。但是,合法 操作必须满足:涂色后这个格子是 好线段的一个端点 (好线段可以是水平的,竖直的或者是对角线)。

好线段 指的是一个包含 三个或者更多格子(包含端点格子)的线段,线段两个端点格子为 同一种颜色 ,且中间剩余格子的颜色都为 另一种颜色 (线段上不能有任何空格子)。你可以在下图找到好线段的例子:

给你两个整数 rMove 和 cMove 以及一个字符 color ,表示你正在执行操作的颜色(白或者黑),如果将格子 (rMove, cMove) 变成颜色 color 后,是一个 合法 操作,那么返回 true ,如果不是合法操作返回 false 。

示例 1:

输入:board =

[[“.”,“.”,“.”,“B”,“.”,“.”,“.”,“.”],[“.”,“.”,“.”,“W”,“.”,“.”,“.”,“.”],[“.”,“.”,“.”,“W”,“.”,“.”,“.”,“.”],[“.”,“.”,“.”,“W”,“.”,“.”,“.”,“.”],[“W”,“B”,“B”,“.”,“W”,“W”,“W”,“B”],[“.”,“.”,“.”,“B”,“.”,“.”,“.”,“.”],[“.”,“.”,“.”,“B”,“.”,“.”,“.”,“.”],[“.”,“.”,“.”,“W”,“.”,“.”,“.”,“.”]],

rMove = 4, cMove = 3, color = “B”

输出:true

解释:‘.’,‘W’ 和 ‘B’ 分别用颜色蓝色,白色和黑色表示。格子 (rMove, cMove) 用 ‘X’ 标记。

以选中格子为端点的两个好线段在上图中用红色矩形标注出来了。

示例 2:

输入:board =

[[“.”,“.”,“.”,“.”,“.”,“.”,“.”,“.”],[“.”,“B”,“.”,“.”,“W”,“.”,“.”,“.”],[“.”,“.”,“W”,“.”,“.”,“.”,“.”,“.”],[“.”,“.”,“.”,“W”,“B”,“.”,“.”,“.”],[“.”,“.”,“.”,“.”,“.”,“.”,“.”,“.”],[“.”,“.”,“.”,“.”,“B”,“W”,“.”,“.”],[“.”,“.”,“.”,“.”,“.”,“.”,“W”,“.”],[“.”,“.”,“.”,“.”,“.”,“.”,“.”,“B”]],

rMove = 4, cMove = 4, color = “W”

输出:false

解释:虽然选中格子涂色后,棋盘上产生了好线段,但选中格子是作为中间格子,没有产生以选中格 子为端点的好线段。

提示:

board.length == board[r].length == 8

0 <= rMove, cMove < 8

board[rMove][cMove] == ‘.’

color 要么是 ‘B’ 要么是 ‘W’ 。

题解:

方法:枚举

       

class Solution {
    public boolean checkMove(char[][] board, int rMove, int cMove, char color) {
        for (int a = -1; a <= 1; ++a) {
            for (int b = -1; b <= 1; ++b) {
                if (a == 0 && b == 0) {
                    continue;
                }
                int i = rMove, j = cMove;
                int cnt = 0;
                while (0 <= i + a && i + a < 8 && 0 <= j + b && j + b < 8) {
                    i += a;
                    j += b;
                    if (++cnt > 1 && board[i][j] == color) {
                        return true;
                    }
                    if (board[i][j] == color || board[i][j] == '.') {
                        break;
                    }
                }
            }
        }
        return false;
    }
}

下接:【题解】—— LeetCode一周小结28


相关文章
|
18天前
|
机器学习/深度学习
|
18天前
|
机器学习/深度学习
|
18天前
|
人工智能 BI
|
3月前
|
机器人
|
4月前
|
人工智能 BI 测试技术
|
6月前
|
人工智能 BI
|
7月前
|
存储 测试技术 索引
【题解】—— LeetCode一周小结21
LeetCode每日一道一周小结21