LeetCode 第 59 场力扣夜喵双周赛(最短路径数+迪杰斯特拉动态规划+最长公共前缀问题) / 第255场周赛(二进制转换,分组背包,子集还原数组(脑筋急转弯))

Posted Zephyr丶J

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LeetCode 第 59 场力扣夜喵双周赛(最短路径数+迪杰斯特拉动态规划+最长公共前缀问题) / 第255场周赛(二进制转换,分组背包,子集还原数组(脑筋急转弯))相关的知识,希望对你有一定的参考价值。

第 59 场力扣夜喵双周赛

两道400多五百,后两道都写出代码来了,但是都有问题,哭辽…
还有刚开始第一道测试好慢,搞心态了

5834. 使用特殊打字机键入单词的最少时间

有一个特殊打字机,它由一个 圆盘 和一个 指针 组成, 圆盘上标有小写英文字母 ‘a’ 到 ‘z’。只有 当指针指向某个字母时,它才能被键入。指针 初始时 指向字符 ‘a’ 。

每一秒钟,你可以执行以下操作之一:

将指针 顺时针 或者 逆时针 移动一个字符。
键入指针 当前 指向的字符。
给你一个字符串 word ,请你返回键入 word 所表示单词的 最少 秒数 。

示例 1:

输入:word = “abc”
输出:5
解释:
单词按如下操作键入:
-花 1 秒键入字符 ‘a’ in 1 ,因为指针初始指向 ‘a’ ,故不需移动指针。
-花 1 秒将指针顺时针移到 ‘b’ 。
-花 1 秒键入字符 ‘b’ 。
-花 1 秒将指针顺时针移到 ‘c’ 。
-花 1 秒键入字符 ‘c’ 。

示例 2:

输入:word = “bza”
输出:7
解释:
单词按如下操作键入:
-花 1 秒将指针顺时针移到 ‘b’ 。
-花 1 秒键入字符 ‘b’ 。
-花 2 秒将指针逆时针移到 ‘z’ 。
-花 1 秒键入字符 ‘z’ 。
-花 1 秒将指针顺时针移到 ‘a’ 。
-花 1 秒键入字符 ‘a’ 。

示例 3:

输入:word = “zjpc”
输出:34
解释:
单词按如下操作键入:
-花 1 秒将指针逆时针移到 ‘z’ 。
-花 1 秒键入字符 ‘z’ 。
-花 10 秒将指针顺时针移到 ‘j’ 。
-花 1 秒键入字符 ‘j’ 。
-花 6 秒将指针顺时针移到 ‘p’ 。
-花 1 秒键入字符 ‘p’ 。
-花 13 秒将指针逆时针移到 ‘c’ 。
-花 1 秒键入字符 ‘c’ 。

提示:

1 <= word.length <= 100
word 只包含小写英文字母。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-time-to-type-word-using-special-typewriter
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题目描述

就是转动呗,比较顺时针还是逆时针转步数少,想像时钟,12个数,0到6,半数是分界线

class Solution {
    public int minTimeToType(String word) {
        int l = word.length();
        int count = l;
        char c = 'a';
        for(int i = 0; i < l; i++){
            char temp = word.charAt(i);
            if(Math.abs(temp - c) < 14)
                count += Math.abs(temp - c);
            else{
                count += 26 - Math.abs(temp - c);
            }
            c = temp;
        }
        return count;
    }
}

5835. 最大方阵和

题目描述

给你一个 n x n 的整数方阵 matrix 。你可以执行以下操作 任意次 :

选择 matrix 中 相邻 两个元素,并将它们都 乘以 -1 。
如果两个元素有 公共边 ,那么它们就是 相邻 的。

你的目的是 最大化 方阵元素的和。请你在执行以上操作之后,返回方阵的 最大 和。

示例 1:

输入:matrix = [[1,-1],[-1,1]]
输出:4
解释:我们可以执行以下操作使和等于 4 :
-将第一行的 2 个元素乘以 -1 。
-将第一列的 2 个元素乘以 -1 。

示例 2:

输入:matrix = [[1,2,3],[-1,-2,-3],[1,2,3]]
输出:16
解释:我们可以执行以下操作使和等于 16 :
-将第二行的最后 2 个元素乘以 -1 。

提示:

n == matrix.length == matrix[i].length
2 <= n <= 250
-10^5 <= matrix[i][j] <= 10^5

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-matrix-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

因为第二题,肯定不会让一个个交换的
所以,还是贪心
可以发下,两个相邻数字都乘以-1,如果两个数字都是负数,那么就会变成都是正数,我们期望都是这样的;如果一个正,一个负,那么就可以将负数移动位置;
通过这个规律,如果有偶数个负数,那么通过交换,都可以将负数两两配对,然后最大和就是所有数的绝对值
如果有奇数个负数,那么通过交换,可以使负数只剩下一个,并且可以使这个负数是绝对值最小的数,所以最大和就是整个矩阵的绝对值和,减去这个负数绝对值×2

class Solution {
    public long maxMatrixSum(int[][] matrix) {
        //想想
        int min = Integer.MAX_VALUE;
        int n = matrix.length;
        long sum = 0;
        int count = 0;
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++){
                count += matrix[i][j] > 0 ? 0 : 1;
                min = Math.min(matrix[i][j] > 0 ? matrix[i][j] : -matrix[i][j], min);
                sum += matrix[i][j] > 0 ? matrix[i][j] : -matrix[i][j];
            }
        }
        return count % 2 == 0? sum : sum - min - min;
    }
}

5836. 到达目的地的方案数

题目描述

你在一个城市里,城市由 n 个路口组成,路口编号为 0 到 n - 1 ,某些路口之间有 双向 道路。输入保证你可以从任意路口出发到达其他任意路口,且任意两个路口之间最多有一条路。

给你一个整数 n 和二维整数数组 roads ,其中 roads[i] = [ui, vi, timei] 表示在路口 ui 和 vi 之间有一条需要花费 timei 时间才能通过的道路。你想知道花费 最少时间 从路口 0 出发到达路口 n - 1 的方案数。

请返回花费 最少时间 到达目的地的 路径数目 。由于答案可能很大,将结果对 109 + 7 取余 后返回。

示例 1:

输入:n = 7, roads = [[0,6,7],[0,1,2],[1,2,3],[1,3,3],[6,3,3],[3,5,1],[6,5,1],[2,5,1],[0,4,5],[4,6,2]]
输出:4
解释:从路口 0 出发到路口 6 花费的最少时间是 7 分钟。
四条花费 7 分钟的路径分别为:
- 0 ➝ 6
- 0 ➝ 4 ➝ 6
- 0 ➝ 1 ➝ 2 ➝ 5 ➝ 6
- 0 ➝ 1 ➝ 3 ➝ 5 ➝ 6

示例 2:

输入:n = 2, roads = [[1,0,10]]
输出:1
解释:只有一条从路口 0 到路口 1 的路,花费 10 分钟。

提示:

1 <= n <= 200
n - 1 <= roads.length <= n * (n - 1) / 2
roads[i].length == 3
0 <= ui, vi <= n - 1
1 <= timei <= 109
ui != vi
任意两个路口之间至多有一条路。
从任意路口出发,你能够到达其他任意路口。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/number-of-ways-to-arrive-at-destination
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

dfs肯定会超时,但是我想的是先写个dfs,然后记忆化优化一下,结果想了半天也不知道怎么优化
然后也想到了最短路,但是感觉最短路都是求到所有点的路,感觉用不上,就跳了
当时的dfs代码,十几个例子就超时了:

class Solution {
    long min = 0;
    int count = 0;
    Map<Integer, Map<Integer, Integer>> map;
    int n;
    boolean[] used;
    int MOD = (int)1e9 + 7;
    long[][] memo;
    public int countPaths(int n, int[][] roads) {
        //先写个暴力看看行不行吧
        this.n = n;
        int l = roads.length;
        used = new boolean[n];
        map = new HashMap<>();
        memo = new long[n][n];   //两点之间花费的时间
        for(int i = 0; i < n; i++){
            for(int j = 0; j < n; j++){
                memo[i][j] = Long.MAX_VALUE;
            }
        }
        for(int i = 0; i < l; i++){
            int[] temp = roads[i];
            Map<Integer, Integer> in = map.getOrDefault(temp[0], new HashMap<>());
            in.put(temp[1], temp[2]);
            map.put(temp[0], in);
            Map<Integer, Integer> in2 = map.getOrDefault(temp[1], new HashMap<>());
            in2.put(temp[0], temp[2]);
            map.put(temp[1], in2);

        }
        dfs(0, 0);
        return count;
    }
    
    public void dfs(int k, long time){
        if(k == n - 1){
            if(min == 0 || time < min){
                min = time;
                count = 1;
            }
            else if(time == min){
                count += 1;
            }
            return;
        }
        Map<Integer, Integer> temp = map.get(k);
        for(int r : temp.keySet()){
            if(used[r])
                continue;
            if(min != 0 && time + temp.get(r) > min)
                continue;
            used[r] = true;
            dfs(r, time + temp.get(r));
            used[r] = false;
        }
    }
}

不会做,想不到咋做?
看了官解,然后又看了讨论里写的答案,仔细思考了一下
说到底,是先求最短路,再统计最短路的数量
而迪杰斯特拉本来就是求到每个节点最短路的算法,而在到达最短路的同时,也可以用一个数组记录到达每个节点最短路的数量大小

先写了个这个代码,然后第45个例子报错了,原因是因为超整数范围了

class Solution {
    public static final int MOD = (int)1e9 + 7;
    public int countPaths(int n, int[][] roads) {
        //先用迪杰斯特拉处理,计算出到每个点的最短路
        //然后再用最短路创建一个有向无环图,然后再图上dfs找到达n-1最短路的数目
        //但是迪杰斯特拉求最短路的时候,就可以用一个数组记录达到每个点最短路的数目


        //首先还是要预处理每两个点之间的距离
        int l = roads.length;
        if(n == 1)
            return 1;
        Map<Integer, Map<Integer, Integer>> map = new HashMap<>();
       
        for(int i = 0; i < l; i++){
            int[] temp = roads[i];
            Map<Integer, Integer> in = map.getOrDefault(temp[0], new HashMap<>());
            in.put(temp[1], temp[2]);
            map.put(temp[0], in);
            Map<Integer, Integer> in2 = map.getOrDefault(temp[1], new HashMap<>());
            in2.put(temp[0], temp[2]);
            map.put(temp[1], in2);
        }

        int[] dist = new int[n];    //到达每个点的最小路径距离
        Arrays.fill(dist, Integer.MAX_VALUE);
        int[] count = new int[n];   //每个点最短路的数目
        //从0到0路径一条
        count[0] = 1;
        
        //迪杰斯特拉算法
        //存放点和距离
        PriorityQueue<int[]> pq = new PriorityQueue<>((a,b) -> (a[1] == b[1] ? a[0] - b[0] : a[1] - b[1]));
        //初始,从0点到达0的距离是0
        pq.offer(new int[]{0, 0});
        while(!pq.isEmpty()){
            int[] top = pq.poll();
            int p = top[0];
            int d = top[1];
            //System.out.print(p + " ");
            //System.out.println(d);

            //如果超过了最短路径,那么就跳过
            if(d > dist[p])
                continue;
            //取出与p相连的节点,把它们都加到优先队列中,距离要累计
            Map<Integer, Integer> rlt = map.get(p);
            for(int key : rlt.keySet()){
                //如果距离大于最短距离,跳过;
                if(rlt.get(key) + d > dist[key])
                    continue;
                //如果等于最短路,那么通过这条路到达最短路的路径数就是count[p]
                if(rlt.get(key) + d == dist[key]){
                    count[key] = (count[key] + count[p]) % MOD;
                }else{
                    //如果小于最短路,那么就加入优先队列
                    pq.offer(new int[]{key, rlt.get(key) + d});
                    //更新最短路,同时更新count数组
                    dist[key] = rlt.get(key) + d;
                    count[key] = (count[key] + count[p]) % MOD;;
                }
            }
        }

        return count[n - 1];
    }
}

然后我就想都改成long就完事了呗,然后:

PriorityQueue<long[]> pq = new PriorityQueue<>((a,b) -> (a[1] == b[1] ? a[0] - b[0] : a[1] - b[1]));

这样写,一直出错,放到idea里一看,原因是因为,compare函数,也就是默认用的比较函数的返回值是int,这样写的话,相减结果是long,所以要转换成int
也就是这样:

PriorityQueue<long[]> pq = new PriorityQueue<>((a,b) -> (int)(a[1] == b[1] ? a[0] - b[0] : a[1] - b[1]));

以为这样就可以了,结果一提交,变成过了44个,醉了,我是不知道咋改了

然后看到个这样的代码,全部用的int,还能过,咱也不知道为啥了,就这样吧,思路懂了就行了

class Solution {
     public int countPaths(int n,int[][] times) {
        final int INF = Integer.MAX_VALUE;
        List<int[]>[] g = new List[n];
        for (int i = 0; i < n; ++i) {
            g[i] = new ArrayList<int[]>();
        }
        for (int[] t : times) {
            int x = t[0] , y = t[1];
            g[x].add(new int[]{y, t[2]});
            g[y].add(new int[]{x, t[2]});
        }
        int mod=1000000007;
        int[][] dist = new int[n][2];
        for (int i = 0; i < n; i++) {
            dist[i][0] = INF;
        }
        
        dist[0][0] = 0;
        dist[0][1] = 1;

        int ans = 0;
        PriorityQueue<int[]> pq = new PriorityQueue<int[]>((a, b) -> a[0] != b[0] ?以上是关于LeetCode 第 59 场力扣夜喵双周赛(最短路径数+迪杰斯特拉动态规划+最长公共前缀问题) / 第255场周赛(二进制转换,分组背包,子集还原数组(脑筋急转弯))的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode第 57 场力扣夜喵双周赛(差分数组单调栈) and 第 251 场力扣周赛(状态压缩动规,树的序列化,树哈希,字典树)

LeetCode494. 目标和 / 474. 一和零 / 203. 移除链表元素 / 第 244 场力扣周赛

第 256 场力扣周赛(状态压缩+dp,二进制子序列的动规940)

第 254 场力扣周赛(KMP贪心快速幂二分+多源bfs并查集 + 时光倒流)

力扣-第52场双周赛

解题报告力扣 第 74 场双周赛