Leetcode - 周赛397

目录

一,3146. 两个字符串的排列差

二,3147. 从魔法师身上吸取的最大能量

三,3148. 矩阵中的最大得分

四,3149. 找出分数最低的排列


一,3146. 两个字符串的排列差

本题就是求同一个字符在两个字符串中的下标之差的绝对值的和,可以使用数组来统计字符的下标,再求下标之差的绝对值的和。

代码如下:

class Solution {
    public int findPermutationDifference(String s, String t) {
        int[] cnt = new int[26];
        int idx = 0;
        for(char ch : s.toCharArray()){
            cnt[ch-'a'] = idx++; 
        }
        idx = 0;
        int ans = 0;
        for(char ch : t.toCharArray()){
            ans += Math.abs(cnt[ch-'a']-idx++);
        }
        return ans;
    }
}

二,3147. 从魔法师身上吸取的最大能量

本题实际上就是把energy数组分成k段,求每一段中连续子数组的最大值(开始位置随便,结束位置必须是这一段的结尾),所以可以枚举每一段的结束位置,倒着往前遍历,求最大值,有点像希尔排序。画个图理解一下:

代码如下:

class Solution {
    public int maximumEnergy(int[] energy, int k) {
        int n = energy.length;
        int ans = Integer.MIN_VALUE;
        for(int i=n-1; i>=n-k; i--){
            int res = 0;
            for(int j=i; j>=0; j-=k){
                res += energy[j];
                ans = Math.max(res, ans);
            }
        }
        return ans;
    }
}

三,3148. 矩阵中的最大得分

写本题前有一点要注意到:A -> B -> C 的得分和 A -> C 的得分是一样的,也就是说,只需要关注起点和终点的差就行,即只需要找到NxM的矩阵中的子矩形它的 终点 - 起点 最大就行。

所以这题就有点类似于求二维前缀和,只不过这里是求二维最小值f[i][j],而答案就是 g[i][j] - Math.min( f[i-1][j],f[i][j-1]),中的最大值

注意:题目要求必须移动一次,所以不能使用 g[i][j] - f[i][j] 来求最大值,可能出现原地不动的情况

class Solution {
    //f[i+1][j+1]:表示(0,0)->(i,j)这个矩阵的最小值
    public int maxScore(List<List<Integer>> grid) {
        int n = grid.size(), m = grid.get(0).size();
        int[][] f = new int[n+1][m+1];
        for(int i=0; i<n+1; i++) f[i][0] = Integer.MAX_VALUE;
        for(int j=0; j<m+1; j++) f[0][j] = Integer.MAX_VALUE;
        int ans = Integer.MIN_VALUE;
        for(int i=0; i<n; i++){
            for(int j=0; j<m; j++){
                f[i+1][j+1] = Math.min(f[i+1][j], f[i][j+1]);
                ans = Math.max(ans, grid.get(i).get(j) - f[i+1][j+1]);
                f[i+1][j+1] = Math.min(f[i+1][j+1], grid.get(i).get(j));
            }
        }
        return ans;
    }
}

四,3149. 找出分数最低的排列

按照暴力的思路来看,枚举[0,.....,n]的所有排列顺序,(之前使用过的后面就不能再次使用),计算出其中分数最低且字典序最小的排序。所以使用 dfs 需要两个参数,一个是之前已使用过的数(这是无序的,只有相邻的数才会对分数产生影响),二是当前位置的前一个数(这是为了计算它的分数)。

  • dfs(i,j):表示在选择过 i 集合(使用二进制来表示集合)中的数,且前一个数为 j 的最低的分数。
  • 从小到大枚举下一个可以选择的数 k,res = Math.max( res, dfs(i|1<<k, k) + Math.abs( k - nums[k]) )
  • 当所有的数都选过之后,直接 return Math.abs(j - nums[0])

起始位置一定可以等于 0:实际上这里只要 perm[i] 与 nums[perm[i+1]] 的对应关系不变,起始位置可以是任何数,只不过题目要求字典序最小,所以这里起始位置为 0。画个图理解一下:

另外一点,题目不是要求返回最小值,而是要返回perm数组,所以还需要多写一步,获取perm数组的方法直接看代码注释。

代码如下:

class Solution {
    int[] nums;
    int n;
    int[][] memo;
    public int[] findPermutation(int[] nums) {
        this.n = nums.length;
        this.nums = nums;
        memo = new int[1<<14][14];
        for(int[] row : memo)
            Arrays.fill(row, -1);
        int[] ans = new int[n];
        print_dfs(1, 0, ans, 0);//1:表示集合中有0
        return ans;
    }
    int dfs(int i, int j){
        if(i == (1<<n)-1){
            return Math.abs(j-nums[0]);
        }
        if(memo[i][j]!=-1) return memo[i][j];
        int res = Integer.MAX_VALUE;
        for(int x=1; x<n; x++){//枚举可取的数
            if((i>>x&1)==0)//该数没有被选择过
                res = Math.min(res, dfs(i|1<<x, x) + Math.abs(j - nums[x]));
        }
        return memo[i][j] = res;
    }
    void print_dfs(int i, int j, int[] ans, int idx){
        ans[idx] = j;//记录数组
        if(i == (1<<n)-1){
            return;
        }
        int final_ans = dfs(i, j);//找到(i,j)最小的值
        for(int x=1; x<n; x++){//从小到大遍历,确保字典序最小
            if((i>>x&1)==1)
                continue;
            if(final_ans == dfs(1<<x|i, x) + Math.abs(j-nums[x])){//如果值相同,说明当前数选择的就是x
                print_dfs(i|1<<x, x, ans, idx+1);
                break;
            }
        }
    }
}
  • 24
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一叶祇秋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值