【力扣周赛】第 113 场双周赛(贪心&异或性质&换根DP)

这篇具有很好参考价值的文章主要介绍了【力扣周赛】第 113 场双周赛(贪心&异或性质&换根DP)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

竞赛链接

https://leetcode.cn/contest/biweekly-contest-113/

Q1:8039. 使数组成为递增数组的最少右移次数

https://leetcode.cn/problems/minimum-right-shifts-to-sort-the-array/

【力扣周赛】第 113 场双周赛(贪心&异或性质&换根DP),算法刷题记录,leetcode,算法,双周赛,力扣,贪心,换根DP

提示:

1 <= nums.length <= 100
1 <= nums[i] <= 100
nums 中的整数互不相同。

竞赛时代码——枚举答案

因为数据范围很小,所以可以从小到大枚举可能的答案。

class Solution {
    public int minimumRightShifts(List<Integer> nums) {
        int n = nums.size();
        // a 是排好序之后的数组,作为标准答案
        int[] a = new int[n];
        for (int i = 0; i < n; ++i) a[i] = nums.get(i);
        Arrays.sort(a);
        // 枚举答案,即枚举右移次数
        for (int x = 0; x < n; ++x) {
            boolean f = true;
            // 检查这个答案下每一位是否移动后相等
            for (int i = 0; i < n; ++i) {
                if (nums.get(i) != a[(i + x) % n]) {
                    f = false;
                    break;
                }
            }
            if (f) return x;
        }
        return -1;
    }
}

Q2:2856. 删除数对后的最小数组长度

https://leetcode.cn/problems/minimum-array-length-after-pair-removals/
【力扣周赛】第 113 场双周赛(贪心&异或性质&换根DP),算法刷题记录,leetcode,算法,双周赛,力扣,贪心,换根DP

提示:
1 <= nums.length <= 10^5
1 <= nums[i] <= 10^9
nums 是 非递减 数组。

竞赛时代码——贪心+优先队列

首先贪心地想,能匹配就匹配。
但是对于样例 [2, 3, 5, 4] 来说,2 和 3 匹配之后,5 和 4就不能匹配了。
所以在 2 和 3 匹配之后,当枚举到 5 时,可以使用 5 替换掉 3,重新将 3 放入待匹配队列中。

具体算法如下:


使用两个优先队列维护已经被枚举过的数值。
pq1 维护等待匹配的较小数字,pq2 维护已经匹配过的较大数字。

分情况讨论:

  1. 当前数字比 pq1 中的数字大时,就将 pq1 中最小的数字删除,两者完成配对,当前数字放入 pq2。
  2. 当前 pq1 中没有数字,即前面没有元素等待配对时,将当前数字与 pq2 中最小的数字比较,如果 pq2 中的数字较小,就使用当前数字替换 pq2 中的数字与前面配对,同时 pq2 中这个最小的数字就多余了,将其放入 pq1 中等待后序的匹配。
  3. 当前数字无法处理时,就放入 pq1 等待后面出现更大的数字时删除。
class Solution {
    public int minLengthAfterRemovals(List<Integer> nums) {
        int cnt = 0;    // 记录删除了几个数字
        // pq1记录较小的数字,pq2记录较大的数字
        PriorityQueue<Integer> pq1 = new PriorityQueue<>(), pq2 = new PriorityQueue<>();
        for (int x: nums) {
            // 如果当前数字比之前出现的 还没被删除过的数字 大
            if (!pq1.isEmpty() && x > pq1.peek()) {
                cnt += 2;
                pq1.poll();
                pq2.offer(x);
            } else {
                if (pq1.isEmpty() && !pq2.isEmpty() && x > pq2.peek()) {
                    // 如果较小的数字没有了 且 当前数字比已经删除的较大的数字大,就替换一下,将之前较大的数字放入较小的数字组中
                    pq1.offer(pq2.poll());
                    pq2.offer(x);
                } else pq1.offer(x);
            }
        }
        return nums.size() - cnt;
    }
}

解法2——贪心+二分查找 O ( log ⁡ N ) O(\log{N}) O(logN)

https://leetcode.cn/problems/minimum-array-length-after-pair-removals/solutions/2446146/olog-n-tan-xin-er-fen-cha-zhao-pythonjav-t3qn/
【力扣周赛】第 113 场双周赛(贪心&异或性质&换根DP),算法刷题记录,leetcode,算法,双周赛,力扣,贪心,换根DP
m a x C n t ∗ 2 < = n maxCnt * 2 <= n maxCnt2<=n 时,最后的结果只和 n 的奇偶性有关。
所以我们只需要考虑 maxCnt 超过半数的情况,此时有序序列 nums 的中间位置元素 x 一定是出现次数最多的元素。
使用二分查找可以找到元素 x 出现的次数。

class Solution {
    public int minLengthAfterRemovals(List<Integer> nums) {
        int n = nums.size();
        int x = nums.get(n / 2);
        // 只需考虑maxCnt*2>n的情况,其它情况剩下的数字数量只和n有关
        int maxCnt = lowerBound(nums, x + 1) - lowerBound(nums, x);
        return Math.max(maxCnt * 2 - n, n % 2);
    }

    // 找nums中最后一个比target小的位置
    public int lowerBound(List<Integer> nums, int target) {
        int l = -1, r = nums.size() - 1;
        while (l < r) {
            int mid = l + r + 1 >> 1;
            if (nums.get(mid) < target) l = mid;
            else r = mid - 1;
        }
        return l;
    }
}

Q3:6988. 统计距离为 k 的点对

https://leetcode.cn/problems/count-pairs-of-points-with-distance-k/
【力扣周赛】第 113 场双周赛(贪心&异或性质&换根DP),算法刷题记录,leetcode,算法,双周赛,力扣,贪心,换根DP

提示:
2 <= coordinates.length <= 50000
0 <= xi, yi <= 10^6
0 <= k <= 100

竞赛时代码——异或性质+哈希表

可以看到数据范围很怪,是 50000,而 k 的数据范围比较小,是 100。我们可以写一个时间复杂度是 O ( n ∗ k ) O(n * k) O(nk) 的算法。

将已经枚举过的 x 和 y 放入哈希表中。
对于一个新的 x 和 y,他要和另外的坐标匹配之和为 k,最多有 k 中可能,即 —— 0 + k, 1 + (k - 1),2 + (k - 2),… ,k + 0。枚举每种情况即可。

根据异或的性质,有 x ^ (i ^ x) = i, y ^ ((k - i) & y) = k - i,因此与 坐标 (x, y) 可以匹配的坐标是 (i ^ x, (k - i) ^ y),其中 i 的取值范围是 0 ~ k。

class Solution {
    public int countPairs(List<List<Integer>> coordinates, int k) {
        int ans = 0;
        Map<Integer, Map<Integer, Integer>> cnt = new HashMap<>();
        for (List<Integer> c: coordinates) {
            int x = c.get(0), y = c.get(1);
            // 枚举 x 和 y 异或取值分配的所有可能。
            for (int i = 0; i <= k; ++i) {
                ans += cnt.getOrDefault(i ^ x, new HashMap<>()).getOrDefault((k - i) ^ y, 0);
            }
            // 将当前坐标放入哈希表
            if (!cnt.containsKey(x)) cnt.put(x, new HashMap<>());
            cnt.get(x).merge(y, 1, Integer::sum);
        }
        return ans;
    }
}

更多有关异或的题目可见:异或/XOR部分问题汇总

Q4:100041. 可以到达每一个节点的最少边反转次数

【力扣周赛】第 113 场双周赛(贪心&异或性质&换根DP),算法刷题记录,leetcode,算法,双周赛,力扣,贪心,换根DP

提示:
2 <= n <= 10^5
edges.length == n - 1
edges[i].length == 2
0 <= ui == edges[i][0] < n
0 <= vi == edges[i][1] < n
ui != vi
输入保证如果边是双向边,可以得到一棵树。

竞赛时代码——换根DP

第一次 dfs 求各个节点向下需要的反转次数。
第二次 dfs 求答案。

class Solution {
    List<Integer>[] g;
    Set<Integer>[] t;
    int n;
    // 答案,该节点往下传递反转的数量,
    int[] ans, cnt;
    
    public int[] minEdgeReversals(int n, int[][] edges) {
        this.n = n;
        ans = new int[n];
        cnt = new int[n];
        g = new ArrayList[n];
        t = new HashSet[n];
        Arrays.setAll(g, e -> new ArrayList<>());
        Arrays.setAll(t, e -> new HashSet<>());
        
        for (int[] e: edges) {
            int x = e[0], y = e[1];
            g[x].add(y);
            g[y].add(x);
            t[x].add(y);
        }
        
        dfs1(0, -1);        // 求cnt
        ans[0] = cnt[0];
        dfs2(0, -1);        // 求ans
        
        return ans;
    }
    
    public void dfs1(int x, int fa) {
        for (int y: g[x]) {
            if (y != fa) {
                dfs1(y, x);         // 先求cnt[y]
                if (!t[x].contains(y)) cnt[x]++;    // 如果x不能往y走,就+1
                cnt[x] += cnt[y];   
            }
        }
    }
    
    public void dfs2(int x, int fa) {
        for (int y: g[x]) {
            if (y != fa) {
                ans[y] = ans[x];    // 两者的差别只取决于x和y之间边的情况
                if (t[x].contains(y) && !t[y].contains(x)) ans[y]++;
                else if (!t[x].contains(y) && t[y].contains(x)) ans[y]--;
                dfs2(y, x);
            }
        }
    }
}

更多关于换根DP可见:
【算法】换根DP
【LeetCode每日一题合集】2023.7.10-2023.7.16(dfs & 换根DP)

相似题目——2581. 统计可能的树根数目(🐂⭐)

https://leetcode.cn/problems/count-number-of-possible-root-nodes/
【力扣周赛】第 113 场双周赛(贪心&异或性质&换根DP),算法刷题记录,leetcode,算法,双周赛,力扣,贪心,换根DP

提示:
edges.length == n - 1
2 <= n <= 10^5
1 <= guesses.length <= 10^5
0 <= ai, bi, uj, vj <= n - 1
ai != bi
uj != vj
edges 表示一棵有效的树。
guesses[j] 是树中的一条边。
guesses 是唯一的。
0 <= k <= guesses.length

class Solution {
    List<Integer>[] g;
    Set<Long> s = new HashSet<Long>();  // 存储各个guess
    // 节点数目,答案,k,节点0作为根节点猜对的数量
    int n, ans, k, cnt0;

    public int rootCount(int[][] edges, int[][] guesses, int k) {
        this.n = edges.length + 1;
        this.k = k;
        g = new ArrayList[n];
        Arrays.setAll(g, e -> new ArrayList<Integer>());
        for (int[] edge: edges) {
            int x = edge[0], y = edge[1];
            g[x].add(y);
            g[y].add(x);
        }

        for (int[] guess: guesses) {
            // 将两个 4 字节数压缩成一个 8 字节数
            s.add((long) guess[0] << 32 | guess[1]);    
        }

        dfs(0, -1);
        reroot(0, -1, cnt0);
        return ans;
    }

    // 计算 0 为根节点时猜对的次数
    public void dfs(int x, int fa) {
        for (int y: g[x]) {
            if (y != fa) {
                if (s.contains((long) x << 32 | y)) ++cnt0;
                dfs(y, x);
            }
        }
    }

    // 计算各个节点为根节点时猜对的次数
    public void reroot(int x, int fa, int cnt) {
        if (cnt >= k) ++ans;        // 此时已经找到了答案(至少有 k 个,所以是 >= k)
        for (int y: g[x]) {
            if (y != fa) {
                int c = cnt;
                if (s.contains((long) x << 32 | y)) --c;
                if (s.contains((long) y << 32 | x)) ++c;
                reroot(y, x, c);
            }
        }
    }
}

成绩记录

【力扣周赛】第 113 场双周赛(贪心&异或性质&换根DP),算法刷题记录,leetcode,算法,双周赛,力扣,贪心,换根DP

靠自己 AK 了!

【力扣周赛】第 113 场双周赛(贪心&异或性质&换根DP),算法刷题记录,leetcode,算法,双周赛,力扣,贪心,换根DP文章来源地址https://www.toymoban.com/news/detail-733250.html

到了这里,关于【力扣周赛】第 113 场双周赛(贪心&异或性质&换根DP)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【力扣周赛】第 352 场周赛

    第 352 场周赛 2760. 最长奇偶子数组 提示: 1 = nums.length = 100 1 = nums[i] = 100 1 = threshold = 100 因为数据范围特别小,所以怎么暴力都是无所谓的。 继续优化 可以发现,每个满足条件的子数组是不会重叠的, 所以在枚举 l 的时候,每次可以将下一个 l 设置成 r。 代码如下: 2761. 和

    2024年02月12日
    浏览(35)
  • 【力扣周赛】第357场周赛

    题目描述 描述:你的笔记本键盘存在故障,每当你在上面输入字符 ‘i’ 时,它会反转你所写的字符串。而输入其他字符则可以正常工作。 给你一个下标从 0 开始的字符串 s ,请你用故障键盘依次输入每个字符。 返回最终笔记本屏幕上输出的字符串。 示例 1: 示例 2: 提示

    2024年02月13日
    浏览(33)
  • 【力扣周赛】第350场周赛

    题目描述 描述:卡车有两个油箱。给你两个整数,mainTank 表示主油箱中的燃料(以升为单位),additionalTank 表示副油箱中的燃料(以升为单位)。 该卡车每耗费 1 升燃料都可以行驶 10 km。每当主油箱使用了 5 升燃料时,如果副油箱至少有 1 升燃料,则会将 1 升燃料从副油箱

    2024年02月09日
    浏览(55)
  • Leetcode 第 108 场双周赛 Problem C 将字符串分割为最少的美丽子字符串(动态规划)

    Leetcode 第 108 场双周赛 Problem C 将字符串分割为最少的美丽子字符串(动态规划) 题目 给你一个二进制字符串 s ,你需要将字符串分割成一个或者多个 子字符串 ,使每个子字符串都是 美丽 的。 如果一个字符串满足以下条件,我们称它是 美丽 的: 它不包含前导 0 。 它是

    2024年02月15日
    浏览(47)
  • 力扣周赛日记350

    早上兴高采烈起床写周赛,结果写完两题开始坐牢。菜的很。 LeetCode 第 350 场周赛 LeetCode 6901. 总行驶距离 2.1.1 题意 卡车两个油箱,耗油1L行驶10km。油箱A耗5L,油箱B给邮箱A油1L。油箱A空后停止行驶,求可行使距离。 2.1.2 分析 开始想O(1)解法,发现这题主要问题在油箱B给了油箱

    2024年02月10日
    浏览(39)
  • 力扣119双周赛

    模拟 贪心,一个变了下一个肯定不用变 滑动窗扣维持k个 二进制枚举+Floyd –

    2024年02月04日
    浏览(39)
  • LeetCode 双周赛 101,DP/中心位贪心/裴蜀定理/Dijkstra/最小环

    本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问。 大家好,我是小彭。 这周比较忙,上周末的双周赛题解现在才更新,虽迟但到哈。上周末这场是 LeetCode 第 101 场双周赛,整体有点难度,第 3 题似乎比第 4 题还难一些。 2605. 从两个数字数组里生成最

    2023年04月09日
    浏览(46)
  • 竞赛 双周赛

    分段 块定义为网格图中 2 x 2 的一个子矩阵。对于 左上角格子为 [x, y] 的块 ,其中 0 = x m - 1 且 0 = y n - 1 ,包含坐标为 [x, y] ,[x + 1, y] ,[x, y + 1] 和 [x + 1, y + 1] 的格子。 任意一个点 (x, y),包含它的块为 [x-1, y-1], [x-1, y], [x, y-1], [x, y],其中坐标不能越界。 BFS / DFS, 树状数组 方

    2024年02月12日
    浏览(40)
  • 115 双周赛

    给你一个整数 n 和一个下标从 0 开始的字符串数组 words ,和一个下标从 0 开始的数组 groups ,两个数组长度都是 n 。 两个长度相等字符串的 汉明距离 定义为对应位置字符 不同 的数目。 你需要从下标 [0, 1, …, n - 1] 中选出一个 最长子序列 ,将这个子序列记作长度为 k 的 [

    2024年02月08日
    浏览(31)
  • 第 107 场LeetCode双周赛

    A 最大字符串配对数目 显然各字符串对 间匹配的先后顺序不影响最大匹配数目, 可以从后往前遍历数组, 判断前面是否有和当前末尾构成匹配的. B 构造最长的新字符串 记忆化搜索: 定义状态 p a a , b b , a b , l a s t p_{aa,bb,ab,last} p aa , bb , ab , l a s t ​ 为剩余三种字符串分别为aa、

    2024年02月11日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包