第320场周赛赛后分析总结(前三题)

简介: 前言 几个星期没打周赛,手感生疏了好多,果然算法题还是得勤加练习,多找适应比赛的感觉。 同时,第二、三题都是图和树相关的内容,像我这种对这个专题还不熟的也可以借此机会巩固一下。
本文首发于稀土掘金。该平台的作者 逐光而行 也是本人。

前言

几个星期没打周赛,手感生疏了好多,果然算法题还是得勤加练习,多找适应比赛的感觉。
同时,第二、三题都是图和树相关的内容,像我这种对这个专题还不熟的也可以借此机会巩固一下。

数组中不等三元组的数目

给你一个下标从 0 开始的正整数数组 nums 。请你找出并统计满足下述条件的三元组 (i, j, k) 的数目:

  • 0 <= i < j < k < nums.length
  • nums[i]nums[j] 和 nums[k] 两两不同 。

    • 换句话说:nums[i] != nums[j]nums[i] != nums[k] 且 nums[j] != nums[k] 。

返回满足上述条件三元组的数目

这题直接三重for循环暴力求解是最直接的方式,我周赛时也是这么用的。

class Solution {
    public int unequalTriplets(int[] nums) {
        //三重循环暴力破解
        int cnt=0;
        int n=nums.length;
        for(int i=0;i<n;i++){
            for(int j=i+1;j<n;j++){
                for(int k=j+1;k<n;k++){
                    if(nums[i] != nums[j]&&nums[i] != nums[k]&&nums[k] != nums[j]) cnt++;
                }
            }
        }
        return cnt;
    }
}

上述解法的时间复杂度是O(N^3)。

有一种能把时间复杂度降到O(N)的方法(排序后用下标规律找)


class Solution {
    public int unequalTriplets(int[] nums) {
        Arrays.sort(nums);
        int ans = 0, start = 0, n = nums.length;
        for (int i = 0; i < n - 1; i++) {
            if (nums[i + 1] != nums[i]) {
                ans += start * (i - start + 1) * (n - 1 - i);
                start = i + 1;
            }
        }
        return ans;
    }
}

二叉搜索树最近结点查询

给你一个 二叉搜索树 的根节点 root ,和一个由正整数组成、长度为 n 的数组 queries 。

请你找出一个长度为 n 的 二维 答案数组 answer ,其中 answer[i] = [mini, maxi] :

  • mini 是树中小于等于 queries[i] 的 最大值 。如果不存在这样的值,则使用 -1 代替。
  • maxi 是树中大于等于 queries[i] 的 最小值 。如果不存在这样的值,则使用 -1 代替。

返回数组 answer 。

我竞赛时的做法是:得到树的中序遍历数组inor;对于目标数组内的每一个元素,尝试从inor中确认它的左右边界。这个方法总是卡在某些测试样例。

注释掉的是尝试的第一种方法,没注释的是第二种方法。它们有一个共同的特点:
if else太多了,没能准确找出左右边界,且通用性不强,这些语句之间稍微变换一下位置结果就又不同。
所以无论怎么调它都很难调好,只能换种思路寻找突破。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    List<Integer> inor;
    public List<List<Integer>> closestNodes(TreeNode root, List<Integer> queries) {
    //按中序遍历BST可得到一个升序数组
        inor=new ArrayList<>();
        inorder(root);
        int m=inor.size();
        List<List<Integer>> res=new ArrayList<>();
        int n=queries.size();
        for(int i=0;i<n;i++){
            List<Integer> tmp=new ArrayList<>();
            for(int j=0;j<m;j++){
                // //特殊情况
                // if(queries.get(i)<inor.get(0)){
                //     tmp.add(-1);
                //     tmp.add(inor.get(0));
                //     break;
                // }
                // else if(queries.get(i)>inor.get(m-1)){
                //     tmp.add(inor.get(m-1));
                //     tmp.add(-1);
                //     break;
                // }
                // else if(queries.get(i)==inor.get(j)){
                    // tmp.add(inor.get(j));
                    // tmp.add(inor.get(j));
                //     break;
                // }
                // else{
                //     if(queries.get(i)>inor.get(j)&&queries.get(i)<inor.get(j+1)){
                //         tmp.add(inor.get(j));
                //         tmp.add(inor.get(j+1));
                //         break;
                //     }
                // }
                if(queries.get(i)==inor.get(j)){
                    tmp.add(inor.get(j));
                    tmp.add(inor.get(j));
                    break;
                }
                else if(queries.get(i)<inor.get(j)){
                    if(j-1>=0){
                        tmp.add(inor.get(j-1));
                    }
                    else{
                        tmp.add(-1);
                    }
                    tmp.add(inor.get(j));
                    break;
                }
                else if(j==m-1){
                    tmp.add(inor.get(m-1));
                    tmp.add(-1);
                    break;
                }
                else if(queries.get(i)>inor.get(j)) continue;
            }
            res.add(tmp);
        }
        return res;
    }
    
    public void inorder(TreeNode root){
        if(root==null) return ;
        inorder(root.left);
        inor.add(root.val);
        inorder(root.right);
    }
}

赛后看到一位大佬的题解,用的是TreeSet,突然感觉这个数据结构正好符合我的设想!
所以我又自己写了一遍。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    TreeSet<Integer> nums;
    int n,m;
    public List<List<Integer>> closestNodes(TreeNode root, List<Integer> queries) {
        nums=new TreeSet<>();
        inorder(root);
        n=queries.size();
        m=nums.size();
        List<List<Integer>> res=new ArrayList<>();
        for(int i=0;i<n;i++){
            int q=queries.get(i);
            Integer a=nums.floor(q);
            Integer b=nums.ceiling(q);
            List<Integer> tmp=new ArrayList<>();
            if(a==null) tmp.add(-1);
            else tmp.add(a);
            if(b==null) tmp.add(-1);
            else tmp.add(b);
            res.add(tmp);
        }
        return res;

    }

    public void inorder(TreeNode root){
        if(root==null) return ;
        inorder(root.left);
        nums.add(root.val);
        inorder(root.right);
    } 
}

TreeSet毕竟是树形的结构,效率肯定不如HashSet,但是用来对这道题进行模拟还是足够的。

到达首都的最小油耗

给你一棵  n 个节点的树(一个无向、连通、无环图),每个节点表示一个城市,编号从  0 到  n - 1 ,且恰好有  n - 1 条路。 0 是首都。给你一个二维整数数组  roads ,其中  roads[i] = [ai, bi] ,表示城市  ai 和  bi 之间有一条  双向路 。

每个城市里有一个代表,他们都要去首都参加一个会议。

每座城市里有一辆车。给你一个整数 seats 表示每辆车里面座位的数目。

城市里的代表可以选择乘坐所在城市的车,或者乘坐其他城市的车。相邻城市之间一辆车的油耗是一升汽油。

请你返回到达首都最少需要多少升汽油。

这道题主要就是利用roads来建图。

class Solution {
    List<Integer> [] graph;
    int seats;
    long res;
    public long minimumFuelCost(int[][] roads, int seats) {
        this.seats=seats;
        res=0;
        //路数
        int n=roads.length;
        //点数 n+1 范围从0到n
        //建图
        graph=new List [n+1];
        for(int i=0;i<=n;i++){
            graph[i]=new ArrayList<>();
        }
        //存图
        for(int [] road:roads){
            graph[road[0]].add(road[1]);
            graph[road[1]].add(road[0]);
        }
        dfs(0,-1);
        return res;
    }

    public int dfs(int cur,int des){
        int size=1;
        //对于当前结点在图上的每一个相邻结点
        for(int node:graph[cur]){
            if(node!=des){
                //如果还没到达目的地
                size+=dfs(node,cur);
            }
        }
        if(cur>0) res+=(size+seats-1)/seats;
        return size;
    }
}

至于算消耗汽油数,要理解什么情况下才能消耗最少?
把所有城市抽象成图上的点,首都就是图中的树的根。一位乘客坐当前结点上的车或者在它之下的点的顺风车是最好的;但如果ta坐着的车又往下接其他人,再回到该点,就走了重复的路线,耗费了汽油,这不是最优的选择。

image.png

因此,算汽油数就是在算所有结点经过的总的路径长度,只是中间需要一个小的转化$size/seats$。
总的路径长度可以用dfs求得。

相关文章
|
2月前
Leetcode第123场双周赛
在LeetCode的第123场双周赛中,参赛者需解决三个问题。第一题涉及根据给定数组构建三角形并判断其类型,如等边、等腰或不等边,代码实现通过排序简化条件判断。第二题要求找出满足差值为k的好子数组的最大和,解决方案利用前缀和与哈希表提高效率。第三题则需要计算点集中满足特定条件的点对数量,解题策略是对点按坐标排序并检查点对是否满足要求。
13 1
|
2月前
|
Python
湖南大学第十六届程序设计竞赛(重现赛)补题题解(更新中)
湖南大学第十六届程序设计竞赛(重现赛)补题题解(更新中)
16 0
【Leetcode】- 第 29 场双周赛
【Leetcode】- 第 29 场双周赛
|
存储 数据安全/隐私保护
|
并行计算 Java 调度
leetcode第84场双周赛
维护一个unordered_map mp,mp[x]表示类型x的任务最近一次的结束时间。按顺序枚举所有任务,设当前任务类型为。从倒数第二个数开始往前考虑。,执行当前任务之前已经经过了。是有序的,直接把遍历。的结果作为答案即可。把等式两边移项,变为。
83 0
leetcode第84场双周赛
|
Android开发
LeetCode 双周赛 98,脑筋急转弯转不过来!
大家好,我是小彭。 昨晚是 LeetCode 第 98 场双周赛,你参加了吗?这场周赛需要脑筋急转弯,转不过来 Medium 就会变成 Hard,转得过来就变成 Easy。
71 0
L1-079 天梯赛的善良 (20 分)
L1-079 天梯赛的善良 (20 分)
200 0
|
机器人
LeetCode第 86 场双周赛
LeetCode第 86 场双周赛
LeetCode第 86 场双周赛
|
项目管理
第321场周赛赛后总结(前三题)+记录一道有意思的题目
前言 今天早上可能是浏览器出了点故障,一直没法打开力扣官网页面(但别的页面没问题)(别人都能进说明不是官网服务器的问题咯),错过了周赛(不过就算按时参加估计也是陪跑,就先这么安慰自己了),下午发现能进去了,赶紧找个时间补了一下题。
109 0