LeetCode 319 周赛

news2024/11/28 18:51:29

纪念本狗第三次AK!!!

纪念本狗第三次AK!!!

2469. 温度转换

给你一个四舍五入到两位小数的非负浮点数 celsius 来表示温度,以 摄氏度Celsius)为单位。

你需要将摄氏度转换为 开氏度Kelvin)和 华氏度Fahrenheit),并以数组 ans = [kelvin, fahrenheit] 的形式返回结果。

返回数组 ans 。与实际答案误差不超过 1 0 − 5 10^{-5} 105 的会视为正确答案。

注意:

  • 开氏度 = 摄氏度 + 273.15
  • 华氏度 = 摄氏度 * 1.80 + 32.00

提示:

  • 0 <= celsius <= 1000

示例

输入:celsius = 36.50
输出:[309.65000,97.70000]
解释:36.50 摄氏度:转换为开氏度是 309.65 ,转换为华氏度是 97.70 。

思路

简单模拟

// C++
class Solution {
public:
    vector<double> convertTemperature(double celsius) {
        vector<double> ans(2);
        ans[0] = celsius + 273.15;
        ans[1] = celsius * 1.80 + 32.00;
        return ans;
        
    }
};

2470. 最小公倍数为 K 的子数组数目

给你一个整数数组 nums 和一个整数 k ,请你统计并返回 nums子数组 中满足 元素最小公倍数为 k 的子数组数目。

子数组 是数组中一个连续非空的元素序列。

数组的最小公倍数 是可被所有数组元素整除的最小正整数。

提示:

  • 1 <= nums.length <= 1000
  • 1 <= nums[i], k <= 1000

示例

输入:nums = [3,6,2,7,1], k = 6
输出:4
解释:以 6 为最小公倍数的子数组是:
  • [3,6,2,7,1]
  • [3,6,2,7,1]
  • [3,6,2,7,1]
  • [3,6,2,7,1]

思路

暴力+gcd

由于数据范围较小,可以暴力枚举所有的子数组,然后计算子数组的最小公倍数,

// C++
class Solution {
public:
    
    int gcd(int a, int b) {
        return b ? gcd(b, a % b) : a;
    }
    
    int subarrayLCM(vector<int>& nums, int k) {
        int ans = 0, n = nums.size();
        for (int i = 0; i < n; i++) {
            int lcm = nums[i]; // 最小公倍数
            for (int j = i; j < n; j++) {
                lcm = lcm  * nums[j] / gcd(lcm, nums[j]);
                if (lcm == k) ans++;
                else if (lcm > k) break;
            }
        }
        return ans;
    }
};

如果这道题的数据范围较大,则不能用暴力枚举。

可以考虑用双指针,但是从一个区间内去掉一个数,再更新这个区间的最小公倍数,并不是那么好计算,需要用线段树或树状数组进行优化。

一些在区间上的操作是能够合并,也能拆分的。比如一个区间[i, j]的前缀和,从中去掉一个数,或往里添加一个数,都很容易计算出新的前缀和;但是有一些操作是只能合并,无法拆分的,比如一个区间[i, j]的最大值,往里添加一个数,很容易计算出新的最大值,但从中去掉一个数,就无法计算新的最大值了。像这种无法进行拆分的操作,就需要用线段树来进行优化。

2471. 逐层排序二叉树所需的最少操作数目

给你一个 值互不相同 的二叉树的根节点 root

在一步操作中,你可以选择 同一层 上任意两个节点,交换这两个节点的值。

返回每一层按 严格递增顺序 排序所需的最少操作数目。

节点的 层数 是该节点和根节点之间的路径的边数。

提示:

  • 树中节点的数目在范围 [1, 10^5]
  • 1 <= Node.val <= 10^5
  • 树中的所有值 互不相同

示例

输入:root = [1,4,3,7,6,8,5,null,null,null,null,9,null,10]
输出:3
解释:
- 交换 4 和 3 。第 2 层变为 [3,4] 。
- 交换 7 和 5 。第 3 层变为 [5,6,8,7] 。
- 交换 8 和 7 。第 3 层变为 [5,6,7,8] 。
共计用了 3 步操作,所以返回 3 。
可以证明 3 是需要的最少操作数目。

思路

层序遍历+选择排序

要将同一层的节点排序,并且要采用交换,且有最小交换次数。我们可以使用选择排序进行模拟,每次将最小的节点交换到最左侧。用一个小根堆来维护未确认位置的点,并用一个指针标记当前需要确认的位置。

// C+++
typedef pair<int, int> PII;
const int N = 1e5 + 10;
class Solution {
public:
    
    int idx_to_val[N];
    
    int val_to_idx[N];
    
    int minimumOperations(TreeNode* root) {
        int ans = 0;
        queue<TreeNode*> q;
        priority_queue<int, vector<int>, greater<int>> heap;
        q.push(root);
        while (q.size()) {
            memset(idx_to_val, -1, sizeof idx_to_val);
            memset(val_to_idx, -1, sizeof val_to_idx);
            int size = q.size();
            for (int i = 0; i < size; i++) {
                auto x = q.front();
                q.pop();
                heap.push(x->val);
                idx_to_val[i] = x->val;
                val_to_idx[x->val] = i;
                if (x->left != nullptr) q.push(x->left);
                if (x->right != nullptr) q.push(x->right);
            }
            // 左侧边界
            int cur = 0;
            while (heap.size()) {
                int x = heap.top();
                heap.pop();
                if (val_to_idx[x] != cur) {
                    int val = x, o_val = idx_to_val[cur];
                    int idx = val_to_idx[x], o_idx = cur;
                    idx_to_val[idx] = o_val;
                    idx_to_val[o_idx] = val;
                    val_to_idx[val] = o_idx;
                    val_to_idx[o_val] = idx;
                    ans++; // 需要交换
                }
                cur++;
            }
        }
        return ans;
    }
};

这道题的另一个经典做法是:置换环

置换环算法能够求出数组排序需要的最小交换次数。其思想是;对每个元素,将其指向其排序后应该放到的位置,直到首尾相接形成一个环。

具体来说,若一个数,在排序前,其在数组中的位置是a,排序后,其在数组中的位置是b,那么新建一条边,从a指向ba -> b。那么其实,对于每个位置所代表的点x,其都有一条出边一条入边

x的出边指向的点,假设是y,表示:排序前位置x上的数,在排序后其最终的位置是y

x的入边来自的点,假设是w,表示:排序后最终位置是x的数,其排序前的位置是w

容易得知,每个位置,该位置原本的数一定会去到一个最终位置(出边),也会有另一个位置的数的最终位置是该位置(入边)。所以,每个位置的出度和入度都不多不少恰好是1。

这样以来,将所有位置表示为点,将位置的关系用边进行连接,那么最终会产生一个或多个首尾相接的环。

当我们交换两个位置的数时,无非有两种情况

  • 交换的是同一个环里的两个位置
  • 交换的是不同环里的两个位置

我们举一组样例数据:7 1 9 3 2 5 4

排序前: 7 1 9 3 2 5 4
排序后: 1 2 3 4 5 7 9

按照上述规则,画出的图如下

解释如下:

  • 排序前位置为1的数是7,排序后最终位置是6,所以1->6
  • 排序前位置为2的数是1,排序后最终位置是1,所以2->1
  • 排序前位置为3的数是9,排序后最终位置是7,所以3->7
  • 排序前位置为4的数是3,排序后最终位置是3,所以4->3
  • 排序前位置是5的数是2,排序后最终位置是2,所以5->2
  • 排序前位置是6的数是5,排序后最终位置是5,所以6->5
  • 排序前位置是7的数是4,排序后最终位置是4,所以7->4

我们尝试交换位置2和位置6(交换同一个环内的2个位置),交换后,更新的图如下

如果我们交换位置32(交换不同环中的2给位置),交换后,更新的图如下

容易得到如下结论:

  • 若交换同一个环内的2个位置,会将1个环拆分成2个环
  • 若交换不同环内的2个位置,会将2个环合并为1个环

我们最终要得到的状态,是每个位置都是一个自环,即最终要得到n个环。

那么,最少的交换次数,一定是每次交换,都将一个环拆成更小的环,而不是将2个环合并成1个更大的环。

假设一个环内的节点数量为k,则这个环最终将被拆成k个自环,共需要k - 1次拆分,即需要k - 1次交换。

一共有n个环,每个环需要的拆分次数是环中节点的数量减1,则所有环需要的拆分次数就是所有节点的数量减去环的数量

于是这样就能求出最小的交换次数。

由于这种环图的特殊性,每个环都是首尾相连,所以每个环都是一个连通块。故这道题也可以用并查集来做。我们只要按照上述规则,进行集合的合并,并维护一下连通块的数量,即可求得需要的最小交换次数。

// C++
class Solution {
public:
	// 并查集模板
    int find(int x, vector<int>& p) {
        if (x != p[x]) p[x] = find(p[x], p);
        return p[x];
    }

    int calc(vector<int>& v) {
        int n = v.size();
        // 记录元素和原位置的关系
        unordered_map<int, int> idx;
        // 并查集 parent数组
        vector<int> p(n);
        for (int i = 0; i < n; i++) {
            idx[v[i]] = i;
            p[i] = i;
        }
        sort(v.begin(), v.end());
        // 开始做集合合并
        int cnt = n; // 连通块个数
        for (int i = 0; i < n; i++) {
            int a = i, b = idx[v[i]];
            if (find(a, p) != find(b, p)) {
                p[find(a, p)] = find(b, p);
                cnt--; // 合并后连通块减少一个
            }
        }
        return n - cnt; // 最少交换次数
    }

    int minimumOperations(TreeNode* root) {
        queue<TreeNode*> q;
        q.push(root);
        int ans = 0;
        while (q.size()) {
            int sz = q.size();
            vector<int> v(sz);
            for (int i = 0; i < sz; i++) {
                TreeNode* x = q.front();
                q.pop();
                v[i] = x->val;
                if (x->left != nullptr) q.push(x->left);
                if (x->right != nullptr) q.push(x->right);
            }
            ans += calc(v); // 计算这一层的交换次数
        }
        return ans;
    }
};

2472. 不重叠回文子字符串的最大数目

给你一个字符串 s 和一个 整数 k

从字符串 s 中选出一组满足下述条件且 不重叠 的子字符串:

  • 每个子字符串的长度 至少k
  • 每个子字符串是一个 回文串

返回最优方案中能选择的子字符串的 最大 数目。

子字符串 是字符串中一个连续的字符序列。

提示:

  • 1 <= k <= s.length <= 2000
  • s 仅由小写英文字母组成

示例

输入:s = "abaccdbbd", k = 3
输出:2
解释:可以选择 s = "abaccdbbd" 中斜体加粗的子字符串。"aba" 和 "dbbd" 都是回文,且长度至少为 k = 3 。
可以证明,无法选出两个以上的有效子字符串。

思路

中心开花+贪心

先求出以每个点为中心的最短回文串,记录其左端点和右端点。然后将这些回文区间按照左端点排个序,随后挨个取出并计数。这是一种贪心的思想,基于的是要想选出的不重叠字符串数量最多,在尽可能要选择长度短的。

// c++
typedef pair<int, int> PII;
class Solution {
public:
    
    bool isPalin(string s, int i, int j) {
        while (i < j) {
            if (s[i] != s[j]) return false;
            i++;
            j--;
        }
        return true;
    }
    
    int maxPalindromes(string s, int k) {
        int n = s.size();
        vector<PII> v; // 存储所有可能的回文
        // 求出以每个点为中心的最短回文
        for (int i = 0; i < n; i++) {
            // 求奇数长度
            int l = i, r = i;
            bool yes = false;
            while (l >= 0 && r < n && s[l] == s[r]) {
                yes = true;
                if (r - l + 1 >= k) break;
                l--;
                r++;
            }
            if (yes && l >= 0 && r < n && s[l] == s[r] && r - l + 1 >= k) v.push_back({l, r}); 
            // 求偶数长度
            l = i;
            r = i + 1;
            yes = false;
            while (l >= 0 && r < n && s[l] == s[r]) {
                yes = true;
                if (r - l + 1 >= k) break;
                l--;
                r++;
            }
            if (yes && l >= 0 && r < n && s[l] == s[r] && r - l + 1 >= k) v.push_back({l, r}); 
        }
        // 将全部的回文排序
        sort(v.begin(), v.end());
        int ans = 0;
        int r = -1; // 维护当前右端点
        for (auto& p : v) {
            if (r == -1) {
                r = p.second;
                ans++;
            } else if (p.first > r) {
                r = p.second;
                ans++;
            }
        }
        return ans;
    }
};

动态规划

也可以直接用动态规划来做,用g[i][j]表示区间[i, j]是否是回文;f[i]表示从[0, i]中能拆出的满足条件的回文串的最大数量。g的状态一共有n^2,计算需要 O ( n 2 ) O(n^2) O(n2)f的状态也是 n^2,所以总的时间复杂度就是 O ( n 2 ) O(n^2) O(n2),在这道题的数据范围下大概是 1 0 6 10^6 106,不会超时。

// C++
class Solution {
public:
    int maxPalindromes(string s, int k) {
        int n = s.size();
        // g[i][j] 表示[i, j]是否是回文串, 下标从1开始
        vector<vector<bool>> g(n + 1, vector<bool>(n + 1, false));
        // 状态转移, 区间DP, 需要从小到达枚举区间长度
        for (int len = 1; len <= n; len++) {
            for (int i = 1; i + len - 1 <= n; i++) {
                int j = i + len - 1;
                if (len == 1) g[i][j] = true;
                else if (s[i - 1] == s[j - 1] && (len == 2 || g[i + 1][j - 1])) g[i][j] = true;
            }
        }

        // f[i]表示[1, i]区间选出不重叠的子字符串的最大数量,下标从1开始
        vector<int> f(n + 1, 0);
        for (int i = 1; i <= n; i++) {
            f[i] = f[i - 1]; // 第i个位置不选
            // 第i个位置选上, 则枚举可能的回文串的长度, 回文串长度至少是k
            // 注意这里要枚举到 j = 0
            for (int j = i - k; j >= 0; j--) {
                if (g[j + 1][i]) {
                    f[i] = max(f[i], f[j] + 1);
                }
            }
        }
        return f[n];
    }
};

总结

第三次AK,还是有点小激动的!

T1是模拟;T2是数论;T3是层序遍历+选择排序/置换环;T4是回文串题目,其实是2个问题的组合,1是求回文串,2是求不重叠回文子串的最大数目;可以用中心开花求回文串,并用贪心求答案;也可以用更为朴素的动态规划。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/26704.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RK3568平台开发系列讲解(图像篇)JPEG图像处理

🚀返回专栏总目录 文章目录 一、JPEG文件格式和libjpeg编译二、libjpeg接口函数三、JPEG文件解析沉淀、分享、成长,让自己和他人都能有所收获!😄 📢我们今天来讲解JPEG图像处理。 一、JPEG文件格式和libjpeg编译 JPEG的后缀名为.jpg的图像文件。对于图像内容和信息相同…

Windows安装docker踩坑

安装过程中出现一下问题&#xff0c;步骤如下 菜鸟教程安装windows docker https://www.runoob.com/docker/windows-docker-install.html 启动后报错wsl2错误&#xff0c;因为本机运行的是wsl1&#xff0c;进行解决 wsl -l -v查看运行的虚-了拟机的版本以及状态 因为默认运…

一文带你学习,动态规划算法

一文带你学习&#xff0c;动态规划算法1.什么是动态规划&#xff08;DP&#xff09;2.青蛙跳台阶问题暴力递归解法自顶向下的递归解法自底向上的动态规划解法3.动态规划的解题套路穷举分析确定边界确定最优子结构写出状态转移方程4.经典线性DP&#xff1a;数字三角形5.01背包&a…

金鸡奖提名电影《巴林塔娜》,主题曲和腾格尔合作是未卜先知吗

在刚刚结束的第三十五届金鸡奖上&#xff0c;由十月天传媒推送的电影《巴林塔娜》&#xff0c;获得了最佳提名奖的荣誉。根据资料显示&#xff0c;十月天传媒是来自北京的一家公司&#xff0c;也是国内最具专业的综合性文化产业运营机构。 话说十月天传媒的董事长袁熙伯先生&am…

Qt OpenGL(二十四)——Qt OpenGL 核心模式-实现彩色三角形

Qt OpenGL(二十四)——Qt OpenGL 核心模式-实现彩色三角形 我们之前在Qt OpenGL 核心模式版本中,看到了Qt关于OpenGL的例程,是一个旋转的彩色三角形,本篇文章我们就使用OpenGL核心模式,实现这个彩色三角形。 图1 旋转的三角形 一、彩色三角形 上一篇文章(Qt OpenGL 核心…

儿童护眼灯AA好还是AAA好?双十二精选国AA儿童护眼灯

儿童护眼灯选择国AA级好&#xff01; 这是为什么呢&#xff1f;我给大家分析一下&#xff0c;简单来说&#xff0c;我国对台灯的质量要求是根据《读写作业台灯性能要求》中&#xff0c;当中将分为国A和国AA级两种标准&#xff0c;主要是对照度和会走的均匀度进行划分的&#x…

失物招领|基于Web的校园失物招领系统的设计与实现

作者主页&#xff1a;编程千纸鹤 作者简介&#xff1a;Java、前端、Pythone开发多年&#xff0c;做过高程&#xff0c;项目经理&#xff0c;架构师 主要内容&#xff1a;Java项目开发、毕业设计开发、面试技术整理、最新技术分享 收藏点赞不迷路 关注作者有好处 引言&#xff…

Haproxy负载均衡集群

目录 一、HAPorxy简介 二、HAPorxy优点 三、理解四层和七层负载均衡 四、HAProxy与LVS的区别 五、使用Haproxy搭建web群集 1、部署2台web服务器 2、编译安装Haproxy 3、客户端访问测试 六、总结 1、HTTP请求的两种方式 2、haproxy配置文件重要参数说明 一、HAPorxy简介…

c语言:初识指针

初识指针一.指针是什么问题一&#xff1a;指针&#xff0c;指针变量&#xff0c;内存&#xff0c;地址&#xff0c;编号的关系问题二&#xff1a;为什么指针大小是4或8字节二.如何使用指针三.指针类型问题三&#xff1a;既然指针是4个字节&#xff0c;那它是如何放下long类型的…

数据库:Centos7安装解压版mysql5.7图文教程,亲测成功

目录 1、卸载Centos7默认自带的mariadb数据库&#xff0c;避免冲突 2、下载解压版mysql并安装 3、配置mysql 4、mysql客户端访问 Centos7安装mysql5.7解压版完整教程避免踩坑&#xff0c;可以把数据目录和系统目录分开设置。 1、卸载Centos7默认自带的mariadb数据库&#xff0c…

Qt5开发从入门到精通——第十二篇一节(Qt5 事件处理及实例——多线程及简单实例)

提示&#xff1a;欢迎小伙伴的点评✨✨&#xff0c;相互学习c/c应用开发。&#x1f373;&#x1f373;&#x1f373; 博主&#x1f9d1;&#x1f9d1; 本着开源的精神交流Qt开发的经验、将持续更新续章&#xff0c;为社区贡献博主自身的开源精神&#x1f469;‍&#x1f680; 文…

菜鸟Linux(3):环境变量

"Oh heiya New World!" 一、什么是环境变量&#xff1f; 谈起环境变量,也许我们在敲代码的层面上并不关心。在链接的时候,我们从来没有告诉编译器,去哪里找动态库去链接;我们也从来没有告诉进程 执行该进程的用户是谁?以及在命令行解释器时&#xff0c;启动一个进程…

深度学习基础--神经网络(4)参数更新策略,梯度法

导数 导数&#xff1a;表示某个瞬间的变化量&#xff0c;公式定义&#xff1a; df(x)dxlimh→0f(xh)−f(x)h(4.4)\frac{df(x)}{dx} lim_{h \to 0}\frac{f(x h)-f(x)}{h} \tag{4.4} dxdf(x)​limh→0​hf(xh)−f(x)​(4.4) 求导的代码实现&#xff1a; import numpy as np i…

SSM框架-Spring(三)

目录 1 Spring对事务的支持 1.1 引入事务场景 1.2 spring对事务的支持 Spring实现事务的两种方式 Spring事务管理API 1.3 事务属性 1.3.1 事务传播行为 1.3.2 事务隔离级别 1.3.3 事务超时 1.3.4 只读事务 1.3.5 异常回滚事务 1.4 事务的全注解式开发 1.5 声明式事…

玩转SQL:咱们的目标是成为SQL方面的“扫地僧”

引言 (Structured Query Language)标准结构化查询语言简称SQL&#xff0c;编写SQL语句是每位后端开发日常职责中&#xff0c;接触最多的一项工作&#xff0c;SQL是关系型数据库诞生的产物&#xff0c;无论是什么数据库&#xff0c;MySQL、Oracle、SQL Server、DB2、PgSQL....&…

FPGA串口接收Demo

串口接收Demo 简单介绍 在发送数据时将并行数据转换成串行数据来传输&#xff0c;在接收数据时将接收到的串行数据转换成并行数据 空闲状态时&#xff0c;为高电平起始位为一个单位长度低电平&#xff0c;停止位为一个长度高电平 分析 帧格式 8位数据位1位停止位无校验位 …

配电站房监控系统方案

配电站为低压用户配送电能&#xff0c;设有中压进线(可有少量出线)、配电变压器和低压配电装置。计讯物联工业网关下配电站房监控系统方案&#xff0c;24小时对运行设备进行不间断数据采集上传服务器&#xff0c;云平台对接&#xff0c;远程实时在线监控设备运行状态 &#xff…

web前端-javascript-标识符(说明,命名规则、不以数字关键字保留字开头、驼峰命名,补充)

文章目录标识符1. 说明2. 命名规则3. 补充标识符 <!DOCTYPE html> <html><head><meta charset"utf-8" /><title></title><script type"text/javascript">//千万不要这么用/* var if 123;console.log(if); *//…

Linux、阿里云服务器用tomcat部署项目

文章目录一、安装JDK和Tomcat1.1 安装JDK2.2 安装Tomcat二、把项目打包成war包&#xff08;jar也可以&#xff0c;但是有区别&#xff09;三、把war包放进webapps里面四、修改tomcat配置五、修改防火墙和开放端口等设置六、在浏览器访问项目一、安装JDK和Tomcat 1.1 安装JDK …

如果你想跨行转做数据分析师,劝你慎重

随着数字化时代的浪潮&#xff0c;数据分析师成了炽手可热的香饽饽&#xff0c;疫情当下&#xff0c;各行各业的失业人员逐渐增多&#xff0c;所以人人都想转行当数据分析师。作为业内人员&#xff0c;说实话&#xff0c;真的不建议&#xff0c;数据分析师真的不是想象的那么简…