Leetcode 第 364 场周赛题解

Leetcode 第 364 场周赛题解

Leetcode 第 364 场周赛题解

题目1:2864. 最大二进制奇数

思路

贪心。

要得到最大二进制奇数,则高位尽可能放 1,为了是奇数,最后一位必须是 1。

设原字符串的长度为 n,统计原字符串中 '1' 的个数,记为 count_one。

新字符串 = count_one - 1 个 '1' + n - count_one 个 '0' + '1'。

代码

cpp 复制代码
/*
 * @lc app=leetcode.cn id=2864 lang=cpp
 *
 * [2864] 最大二进制奇数
 */

// @lc code=start
class Solution
{
public:
    string maximumOddBinaryNumber(string s)
    {
        int n = s.size(), count_one = 0;
        for (char &c : s)
            if (c == '1')
                count_one++;
        string ans;
        for (int i = 0; i < count_one - 1; i++)
            ans += '1';
        for (int i = 0; i < n - count_one; i++)
            ans += '0';
        ans += '1';
        return ans;
    }
};
// @lc code=end

复杂度分析

时间复杂度:O(n),其中 n 是字符串的长度。

空间复杂度:O(1)。

题目2:美丽塔 I

思路

枚举峰值的位置,向左、向右求高度和,更新高度和的最大值。

代码

cpp 复制代码
/*
 * @lc app=leetcode.cn id=2865 lang=cpp
 *
 * [2865] 美丽塔 I
 */

// @lc code=start
class Solution
{
public:
    long long maximumSumOfHeights(vector<int> &maxHeights)
    {
        int n = maxHeights.size();
        long long max_sum = 0;
        // 枚举峰值的位置
        for (int i = 0; i < n; i++)
        {
            long long cur_sum = maxHeights[i];
            // 向左求和
            int left_height = maxHeights[i];
            for (int j = i - 1; j >= 0; j--)
            {
                left_height = min(left_height, maxHeights[j]);
                cur_sum += left_height;
            }
            // 向右求和
            int right_height = maxHeights[i];
            for (int j = i + 1; j < n; j++)
            {
                right_height = min(right_height, maxHeights[j]);
                cur_sum += right_height;
            }
            // 更新答案
            max_sum = max(max_sum, cur_sum);
        }
        return max_sum;
    }
};
// @lc code=end

复杂度分析

时间复杂度:O(n^2^),其中 n 是数组 maxHeights 的长度。

空间复杂度:O(1)。

题目3:美丽塔 II

思路

思考优化:

以示例 [6,5,3,9,2,7] 为例,我们观察以 3 和 9 作为山顶的两个方案:

  • 以 3 作为山顶:3 3 |3 3| 2 2
  • 以 9 作为山顶:3 3 |3 9| 2 2

可以发现:以 3 作为山顶的左侧与以 9 为山顶的右侧在两个方案之间是可以复用的,至此发现解决方法:我们可以分别预处理出以每个节点作为山顶的前缀和后缀的和:

  • prev[i] 表示以 maxHeights[i] 作为山顶时左侧段的前缀和;
  • suffix[i] 表示以 maxHeights[i] 作为山顶时右侧段的后缀和。

那么,最佳方案就是 prev[i]+suffix[i]−maxHeight[i] 的最大值。 现在,最后的问题是如何以均摊 O(1) 的时间复杂度计算出每个元素前后缀的和?

继续以示例 [6,5,3,9,2,7] 为例:

  • 以 6 为山顶,前缀为 [6]
  • 以 5 为山顶,需要保证左侧元素不大于 555,因此找到 666 并修改为 555,前缀为 [5,5]
  • 以 3 为山顶,需要保证左侧元素不大于 333,因此找到两个 555 并修改为 333,前缀为 [3,3,3]
  • 以 9 为山顶,需要保证左侧元素不大于 999,不需要修改,前缀为 [3,3,3,9]
  • 以 2 为山顶,需要保证左侧元素不大于 222,修改后为 [2,2,2,2,2]
  • 以 7 为山顶,需要保证左侧元素不大于 777,不需要修改,前缀为 [2,2,2,2,2,7]

观察以上步骤,问题的关键在于修改操作:由于数组是递增的,因此修改的步骤就是在「寻找小于等于当前元素 x 的上一个元素」,再将中间的元素削减为 x。「寻找上一个更小元素」,这是单调栈的典型场景。

代码

cpp 复制代码
/*
 * @lc app=leetcode.cn id=2866 lang=cpp
 *
 * [2866] 美丽塔 II
 */

// @lc code=start
class Solution
{
public:
    long long maximumSumOfHeights(vector<int> &maxHeights)
    {
        int n = maxHeights.size();
        vector<long long> suffix(n + 1, 0);
        vector<long long> prev(n + 1, 0);
        stack<int> s;
        // 单调栈求前缀
        for (int i = 0; i < n; i++)
        {
            // 弹出栈顶
            while (!s.empty() && maxHeights[s.top()] > maxHeights[i])
                s.pop();
            int j = s.empty() ? -1 : s.top();
            prev[i + 1] = prev[j + 1] + (long long)(i - j) * maxHeights[i];
            s.push(i);
        }
        // 清空栈
        while (!s.empty())
            s.pop();
        // 单调栈求后缀
        for (int i = n - 1; i >= 0; i--)
        {
            // 弹出栈顶
            while (!s.empty() && maxHeights[s.top()] > maxHeights[i])
                s.pop();
            int j = s.empty() ? n : s.top();
            suffix[i] = suffix[j] + (long long)(j - i) * maxHeights[i];
            s.push(i);
        }
        // 合并
        long long max_sum = 0;
        for (int i = 0; i < n; i++)
            max_sum = max(max_sum, prev[i + 1] + suffix[i] - maxHeights[i]);
        return max_sum;
    }
};
// @lc code=end

复杂度分析

时间复杂度:O(n),其中 n 是数组 maxHeights 的长度。在一侧的计算中,每个元素最多如何和出栈 1 次。

空间复杂度:O(n),其中 n 是数组 maxHeights 的长度。

题目4:统计树中的合法路径数目

超出能力范围。

思路

经典的树型 DP 模型。维护以下值:

  • f~u~ 表示以节点 u 为起点,以 u 的子树中某个节点为终点的路径中,所有点编号都是合数的路径有几条。
  • g~u~ 表示以节点 u 为起点,以 u 的子树中某个节点为终点的路径中,有一个点的编号为质数的路径有几条。

answer += f[u] * g[v] + f[v] * g[u]。

题解:

https://leetcode.cn/problems/count-valid-paths-in-a-tree/solutions/2456568/shu-dp-by-tsreaper-of6v/

【图解】O(n) 线性做法(Python/Java/C++/Go)

代码

cpp 复制代码
/*
 * @lc app=leetcode.cn id=2867 lang=cpp
 *
 * [2867] 统计树中的合法路径数目
 */

// @lc code=start

// 树型 DP

class Solution
{
private:
    #define MAXN (int)1e5
    bool inited = false;
    bool flag[MAXN + 10];

    // 筛法预处理 1e5 以内的质数
    void init()
    {
        if (inited)
            return;
        inited = true;

        memset(flag, 0, sizeof(flag));
        flag[0] = flag[1] = true;
        for (int i = 2; i * i <= MAXN; i++)
            if (!flag[i])
                for (int j = i << 1; j <= MAXN; j += i)
                    flag[j] = true;
    }

public:
    long long countPaths(int n, vector<vector<int>> &edges)
    {
        init();

        vector<int> e[n + 1];
        for (auto &edge : edges)
        {
            e[edge[0]].push_back(edge[1]);
            e[edge[1]].push_back(edge[0]);
        }

        long long ans = 0;
        int f[n + 1], g[n + 1];
        // 树 dp
        function<void(int, int)> dfs = [&](int sn, int fa)
        {
            if (flag[sn])
                f[sn] = 1, g[sn] = 0;
            else
                f[sn] = 0, g[sn] = 1;
            for (int fn : e[sn])
                if (fn != fa)
                {
                    dfs(fn, sn);
                    // 路径上深度最低的节点为 sn,这样的合法路径有几条
                    ans += f[sn] * g[fn] + g[sn] * f[fn];
                    // 更新以 sn 为起点,且走到子树里的路径数量
                    if (flag[sn])
                    {
                        f[sn] += f[fn];
                        g[sn] += g[fn];
                    }
                    else
                    {
                        g[sn] += f[fn];
                    }
                }
        };
        dfs(1, 0);
        return ans;
    }
};
// @lc code=end

复杂度分析

时间复杂度:O(n),其中 n 是数组 edges 的长度。

空间复杂度:O(n),其中 n 是数组 edges 的长度。

相关推荐
爱数模的小云24 分钟前
【华为杯】2024华为杯数模研赛E题 解题思路
算法·华为
白葵新27 分钟前
PCL addLine可视化K近邻
c++·人工智能·算法·计算机视觉·3d
seanli100832 分钟前
线性dp 总结&详解
算法·动态规划
小丁爱养花34 分钟前
记忆化搜索专题——算法简介&力扣实战应用
java·开发语言·算法·leetcode·深度优先
Faris_yzf42 分钟前
物联网LoRa定位技术详解
科技·算法
Crossoads1 小时前
【数据结构】排序算法---快速排序
c语言·开发语言·数据结构·算法·排序算法
DS数模1 小时前
2024华为杯研赛E题保姆级教程思路分析
算法·数学建模·华为·华为杯数学建模·2024华为杯·2024研赛
眰恦3741 小时前
数据结构--第五章树与二叉树
数据结构·算法
Pandaconda1 小时前
【计算机网络 - 基础问题】每日 3 题(十)
开发语言·经验分享·笔记·后端·计算机网络·面试·职场和发展