LeetCode第174场周赛

1337. 方阵中战斗力最弱的 k 行

难度简单6收藏分享切换为英文关注反馈

给你一个大小为 m * n 的方阵 mat,方阵由若干军人和平民组成,分别用 0 和 1 表示。

请你返回方阵中战斗力最弱的 k 行的索引,按从最弱到最强排序。

如果第 i 行的军人数量少于第 j 行,或者两行军人数量相同但 i 小于 j,那么我们认为第 i 行的战斗力比第 j 行弱。

军人 总是 排在一行中的靠前位置,也就是说 1 总是出现在 0 之前。

示例 1:

输入:mat = 
[[1,1,0,0,0],
 [1,1,1,1,0],
 [1,0,0,0,0],
 [1,1,0,0,0],
 [1,1,1,1,1]], 
k = 3
输出:[2,0,3]
解释:
每行中的军人数目:
行 0 -> 2 
行 1 -> 4 
行 2 -> 1 
行 3 -> 2 
行 4 -> 5 
从最弱到最强对这些行排序后得到 [2,0,3,1,4]

        本题的难点在于索引绑定。涉及到有关Java中实现Map按值排序的相关技巧。

    public int[] kWeakestRows(int[][] mat, int k) {
        Map<Integer, Integer> map = new HashMap<>();  //问题:如何建立联系
//        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < mat.length; i++){
            int count = 0;
            for (int j = 0; j < mat[0].length; j++) {
                if (mat[i][j] == 1) {
                    count += 1;
                }
            }
            map.put(i, count);
        }
        List<Map.Entry<Integer, Integer>> list = new ArrayList<>(map.entrySet());
        Collections.sort(list, new Comparator<Map.Entry<Integer, Integer>>() {
            @Override
            public int compare(Map.Entry<Integer, Integer> o1, Map.Entry<Integer, Integer> o2) {
                return o1.getValue().compareTo(o2.getValue());
            }
        });
        int[] res = new int[k];
        for (int i = 0; i < k; i++) {
            Map.Entry<Integer, Integer> entry = list.get(i);
            res[i] = entry.getKey();
        }
        return res;
    }

 

1338. 数组大小减半

给你一个整数数组 arr。你可以从中选出一个整数集合,并删除这些整数在数组中的每次出现。

返回 至少 能删除数组中的一半整数的整数集合的最小大小。

示例 1:

输入:arr = [3,3,3,3,5,5,5,2,2,7]
输出:2
解释:选择 {3,7} 使得结果数组为 [5,5,5,2,2]、长度为 5(原数组长度的一半)。
大小为 2 的可行集合有 {3,5},{3,2},{5,2}。
选择 {2,7} 是不可行的,它的结果数组为 [3,3,3,3,5,5,5],新数组长度大于原数组的二分之一。

        与上一题如出一辙,都是Map按值排序

public int minSetSize(int[] arr) {
        int len = arr.length;
        Map<Integer, Integer> map = new HashMap<>();
        for (int i = 0; i < len; i++) {
            if (map.containsKey(arr[i])) {
                int count = map.get(arr[i]);
                map.put(arr[i], count + 1);
            } else {
                map.put(arr[i], 1);
            }
        }
        List<Map.Entry<Integer, Integer>> list = new ArrayList<>(map.entrySet());
        Collections.sort(list, new Comparator<Map.Entry<Integer, Integer>>() {
            @Override
            public int compare(Map.Entry<Integer, Integer> o1, Map.Entry<Integer, Integer> o2) {
                return o1.getValue().compareTo(o2.getValue());
            }
        });
        int result = 0;
        int sum = 0;
        for (int i = list.size() - 1; i >= 0; i--) {
            Map.Entry<Integer, Integer> entry = list.get(i);
            sum += entry.getValue();
            if (sum < (len / 2)) {
                result++;
            } else {
                return result + 1;
            }
        }
        return result;
    }

 

1339. 分裂二叉树的最大乘积

给你一棵二叉树,它的根为 root 。请你删除 1 条边,使二叉树分裂成两棵子树,且它们子树和的乘积尽可能大。

由于答案可能会很大,请你将结果对 10^9 + 7 取模后再返回。

示例 1:

输入:root = [1,2,3,4,5,6]
输出:110
解释:删除红色的边,得到 2 棵子树,和分别为 11 和 10 。它们的乘积是 110 (11*10)

分析:

        首先要理解分裂,当一棵树分裂以后,其中一个树(1树)的根节点为原本树的根节点,另一个树(2树)的根节点为断裂边的一端节点。那么枚举每一根边断裂的情况,可以发现 任何一个节点(除根节点)均可以作为2树的根节点

  • 题目就转化为遍历每一个子节点,计算以该节点为根节点的树子节点之和。
  • 分别求两棵子树的和显然很难做到,但我们可以很方便的其中一个子树的和
  • 整棵树的总和减去求出的子树和,就得到另一棵子树和
  • 枚举所有子树,对乘积取最大。
public class Solution {
    double ans = Double.MIN_VALUE;
    double allSum;
    double nodeSum;

    public int maxProduct(TreeNode root) {
        allSum = sum(root);
        dfs(root);
        return (int) (ans % (int) (1e9 + 7));
    }

    public double sum(TreeNode node) {
        if (node == null) return 0;
        return node.val + sum(node.left) + sum(node.right);
    }

    public double dfs(TreeNode node) {
        if (node == null) return 0;
        nodeSum = node.val + dfs(node.left) + dfs(node.right);
        ans = Math.max(ans, (allSum - nodeSum) * nodeSum);  //另一棵子树 = 和 - 深搜子树
        return nodeSum;
    }
}

 

1340. 跳跃游戏 V

给你一个整数数组 arr 和一个整数 d 。每一步你可以从下标 i 跳到:

  • i + x ,其中 i + x < arr.length 且 0 < x <= d 。
  • i - x ,其中 i - x >= 0 且 0 < x <= d 。

除此以外,你从下标 i 跳到下标 j 需要满足:arr[i] > arr[j] 且 arr[i] > arr[k] ,其中下标 k 是所有 i 到 j 之间的数字(更正式的,min(i, j) < k < max(i, j))。

你可以选择数组的任意下标开始跳跃。请你返回你 最多 可以访问多少个下标。

请注意,任何时刻你都不能跳到数组的外面。

示例 1:

输入:arr = [6,4,14,6,8,13,9,7,10,6,12], d = 2
输出:4
解释:你可以从下标 10 出发,然后如上图依次经过 10 --> 8 --> 6 --> 7 。
注意,如果你从下标 6 开始,你只能跳到下标 7 处。你不能跳到下标 5 处因为 13 > 9 。你也不能跳到下标 4 处,因为下标 5 在下标 4 和 6 之间且 13 > 9 。
类似的,你不能从下标 3 处跳到下标 2 或者下标 1 处。
class Solution {
    private Integer[] cache;
    public int maxJumps(int[] arr, int d) {
        cache = new Integer[arr.length];
        int res = 0;
        for (int i = 0; i < arr.length; i++) {
            res = Math.max(res, dfs(arr, i, d));
        }
        return res;
    }

    private int dfs(int[] arr, int cur, int d) {
        if (cache[cur] != null) {  //判空,必须为包装类
            return cache[cur];
        }
        // 确定左右边界
        int left = Math.max(0, cur - d);
        int right = Math.min(arr.length - 1, cur + d);
        int step = 0;
        for (int direction = -1; direction <= 1; direction += 2) {  //方向:向左跳/向右跳
            for (int i = cur + direction; i <= right && i >= left; i += direction) {
                if (arr[i] >= arr[cur]) {
                    break;
                }
                step = Math.max(step, dfs(arr, i, d));
            }
        }
        return cache[cur] = step + 1;
    }
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值