Leetcode第319场周赛题解

T1:LeetCode 2469. 温度转换

简单的模拟题:

代码一行搞定

class Solution {
public:
    vector<double> convertTemperature(double celsius) {
        return {celsius + 273.15, celsius * 1.8 + 32};
    }
};

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

 

 发现这里数据范围比较小,直接枚举遍历就好

先枚举起点再枚举终点,枚举终点时边枚举边求最小公倍数

这里 a和b最小公倍数 = a * b / a和b的最大公约数

这样子时间复杂度的话就n^2logn,在数据范围内可以过。

上代码

class Solution {
public:
    int gcd(int a, int b)
    {
        return b ? gcd(b, a % b) : a;
    }
    int subarrayLCM(vector<int>& nums, int k) {
        int res = 0;

        for(int i = 0; i < nums.size(); i ++ ){
            int t = nums[i];//t存当前最小公倍数
            for(int j = i; j < nums.size(); j ++ ){
                t = t * nums[j] / gcd(t, nums[j]);
                if(t == k) res ++ ;
                else if(t > k) break;//及时跳出循环
            }
        }
        return res;
    }
};

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

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

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

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

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

示例 1 :

输入: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 是需要的最少操作数目。

示例 2 :

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

示例 3 :

第三题压轴,感觉比第四题难

共两问:第一问求层数遍历,第二问将区间变成升序,每一次只能操作两个数,问最少操作多少次

第一问dfs,bfs都行,第二问,考的是环图,之前也没见过

在这里好好总结一下:

我们以位置建立图

假设原区间为2,1,4,5,3

这里建图规则为:第一个位置数1在原区间在2的位置,就从1往2连一条边

第二个位置数2在原区间在1的位置,就从2往1连一条边

第三个位置数3在原区间在5的位置,就从3往5连一条边

第四个位置数4在原区间在3的位置,就从4往3连一条边

第五个位置数5在原区间在4的位置,就从5往4连一条边

这样的话规则就是假设 W_{i} 在原数组中是第 j 的位置,那么我们就从 i 向 j 连一条边

保证了每个点的出度和入度都是1,那么必然会形成一个环,必然不会走到中间的点,因为这样某一点的入度就为2。

这样我们可以发现原区间我们可以将它看作是若干个环组成

因此,我们交换数字又有两种情况,第一种情况是交换的数不在一个环里,另一种情况是交换的数在一个环里。

当交换的数不在一个环里时,交换完后就结合成了一个环,如图

交换的数在一个环里时,交换操作会将一个环拆分成两个环,如图

 而我们的目标是将原数组变成升序,也就是变成n个自环。

这样我们的题目就可以理解为:

给我们一堆环图,我们有两种操作

1.合并两个环

2.拆分一个环(拆成两个环)

目标是将环图拆成n个环,问最少要进行多少次操作。

再来分析,想要变成n个环,也就是说要多进行拆分操作,少合并,那我们可以统计一下当前有多少个环,假设有cnt个环的话,每拆一次最多增加一个环,则最少要拆n-cnt次。

最后n-cnt即为答案。

最后最后,我们求得环的数量即可。

上代码

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:

    vector<int> p;

    int find(int x)// 默写并查集
    {
        if(p[x] != x) p[x] = find(p[x]);
        return p[x];
    }
    int minimumOperations(TreeNode* root) {
        queue<TreeNode*> q;
        q.push(root);//插入头节点
        vector<int> w, ls;//再定义数组存储层序遍历的序列,ls代表每一层的起点
        while(q.size())//当队列不空时
        {
            int sz = q.size();//求队列长度
            ls.push_back(w.size());//开始结点为w.size()
            for(int k = 0;k < sz; k ++ )//遍历每一层结点,每层的话有sz个结点
            {
                auto t = q.front();//每次取出来队头元素,接着删掉
                q.pop();
                w.push_back(t->val);//记录点的权值
                //判断如果有左右儿子的话,就遍历一下左右儿子
                if(t->left) q.push(t->left);
                if(t->right) q.push(t->right);
            }
        }

        unordered_map<int, int> pos;//记录每个点在原数组中的位置
        for(int i = 0; i < w.size(); i ++ )
        {
            pos[w[i]] = i;
            p.push_back(i);
        }

        ls.push_back(w.size());//给最后一层加上个终点
        for(int i = 0; i + 1 < ls.size(); i ++ )
            sort(w.begin() + ls[i], w.begin() + ls[i + 1]);//将每一层排个序
        

        int cnt = w.size();//接着统计当前有多少连通块
        for(int i = 0; i < w.size(); i ++ ){
            int a = find(i), b = find(pos[w[i]]);//第一个点为i,第二点为w[i]在原数组中的位置
            if(a != b)//判断一下如果合并的话
            {
                p[a] = b;
                cnt -- ;
            }
        }

        return w.size() - cnt;
    }
};

 第三题值得好好研究一下,代码实现上还用哈希表与并查集的知识。

本体涉及集合的合-->并查集

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

 还是两个问题,第一个问题,预处理一下每个区间是不是回文串,第二个问题求能选择的最大数量用dp做

f(i)代表前 i 个字母最多可以拆出来多少个不重叠的回文串

这里的话就是一个dp问题,

第一种情况就是第i个字母不被选,那么答案就为f(i-1)

接下来的情况就是第i个字母被选,再枚举最后一段长度,如果是1,2,3,.......

当然我们在枚举的时候要考虑大等于k的情况

最后枚举一下最后一段长度,当最后一段长度为 l 时,这一类情况的最大值就是让前面尽可能取最多的回文串,即f(i -l)

具体的看一下代码吧

class Solution {
public:
    int maxPalindromes(string s, int k) {
        int n = s.size();
        vector<vector<bool>> g(n + 1, vector<bool>(n + 1));//下标从1开始
        for (int len = 1; len <= n; len ++ ) {
            for (int i = 1; i + len - 1 <= n; i ++ ) {
                int j = i + len - 1;
                //一个字母的话一定是回文串
                if (s[i - 1] == s[j - 1] && (len <= 2 || g[i + 1][j - 1]))
                    g[i][j] = true;
            }
        }

        vector<int> f(n + 1);
        for (int i = 1; i <= n; i ++ ) {
            f[i] = f[i - 1];
            for (int j = i - k; j >= 0; j -- ) {//保证最少有k个字母
                if (g[j + 1][i]) {//当j + 1 到 i 是回文串
                    f[i] = max(f[i], f[j] + 1);
                }
            }
        }

        return f[n];
    }
};

总结:T2:最大公倍数求法

T3:层序遍历,环图

T4:判断回文,区间dp

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值