刷题周总结(2023.10.26 - 2023.11.1)

2023.10.26

LC2520. 统计能整出数字的位数

  • 题目链接

  • 解题思路:

    • 分解数位,判断每个数位是否能整除num
  • 解题代码:

class Solution {
public:
    int countDigits(int num) {
        int temp = num;
        int ans = 0;
        while(temp)
        {
            int bit = temp % 10;
            ans += (num % bit == 0);
            temp /= 10;
        }
        return ans;
    }
};

CF750C- New Year and Rating

  • 题目链接

  • 解题思路:

    • 如果是参加1类,要求上一场竞赛后最大分 >= 1900

    • 如果是参加2类,要求上一场竞赛后最小分 < 1900

    • 初始化最小分为INT_MIN,最大分为INT_MAX

    • 遍历每一场竞赛:

      • 如果是1类竞赛

        • 最大分小于1900,输出Impossible
        • 最小分更新为max(1900, 最小分)+ c[i],最大分如果不为INT_MAX,更新为最大分 + c[i]
      • 如果是2类竞赛

        • 最小分如果大于等于1900,输出Impossible
        • 最大分更新为min(1899,最大分)+ c[i],最小分如果不为INT_MIN,更新为最小分 + c[i]
    • 如果最大分为INT_MAX,输出Infinity,否则输出最大分

  • 解题代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n = 0;
    cin >> n;
    vector<int> c(n), d(n);
    for(int i = 0; i <n; i++)
        cin >> c[i] >> d[i];
    //如果是参加1类,要求上一场竞赛后最大分 >= 1900
    //如果是参加2类,要求上一场竞赛后最小分 < 1900
    int left = INT_MIN, right = INT_MAX;
    for(int i = 0; i < n; i++)
    {
        if(d[i] == 1)
        {
            if(right < 1900)
            {
                cout << "Impossible" << endl;
                return 0;
            }
            left = max(1900, left) + c[i];
            if(right != INT_MAX)
                right += c[i];
        }
        else if(d[i] == 2)
        {
            if(left >= 1900)
            {
                cout << "Impossible" << endl;
                return 0;
            }
            right = min(right, 1899) + c[i];
            if(left != INT_MIN)
                left += c[i];
           
        }
    }
    if(right == INT_MAX)
        cout << "Infinity" << endl;
    else
        cout << right << endl;
    return 0;
}

ARC134D Concatnate Subsequences

  • 题目链接

  • 解题思路:

    • 先将原始数组拆成a b两个数组,其中各有n个元素
    • 使用单调栈,将a处理成一个非递减数组,满足a[i+1] >= a[i]
    • 令mn 等于所有满足a[i] = a[0] 的i中最小的b[i]
      • 如果mn <= a[0],最小字典序序列就是{a[0],mn}
      • 如果mn > a[0],b[0]一定在最后的最小字典序序列中,分两种情况讨论
        • 将a中大于b[0]的元素都删掉
        • 将a中大于等于b[0]的元素都删掉
        • 比较上述两种情况产生的序列,较小的那个即为答案
  • 解题代码:


#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n = 0;
    cin >> n;
    vector<int> a(n*2);
    for(int i = 0; i < 2 * n; i++)
        cin >> a[i];
    vector<int> a1, a2;
    for(int i = 0; i < n; i++)
    {
        while(!a1.empty() && a1.back() > a[i])
        {    
            a1.pop_back();
            a2.pop_back();
        }
        a1.push_back(a[i]);
        a2.push_back(a[i+n]);
    }
    int mn = INT_MAX;
    int len = a1.size();
    for(int i = 0; i < len; i++)
    {
        if(a1[i] != a1[0])
            break;
        mn = min(mn, a2[i]);
    }
    if(mn <= a1[0])
        cout << a1[0] << " " << mn << endl;
    else
    {
        vector<int> ans1, ans2;
        int idx1 = lower_bound(a1.begin(), a1.end(), a2[0]) - a1.begin();
        for(int i = 0; i < idx1; i++)
            ans1.push_back(a1[i]);
        for(int i = 0; i < idx1; i++)
            ans1.push_back(a2[i]);
       
        int idx2 = upper_bound(a1.begin(), a1.end(), a2[0]) - a1.begin();
        for(int i = 0; i < idx2; i++)
            ans2.push_back(a1[i]);
        for(int i = 0; i < idx2; i++)
            ans2.push_back(a2[i]);
       
        vector<int> ans = min(ans1, ans2);
        for(int i = 0; i < ans.size(); i++)
            cout << ans[i] << " ";
        cout << endl;
    }
    //system("pause");
    return 0;
}


2023.10.27

LC1465. 切割后面积最大的蛋糕

  • 题目链接

  • 解题思路:

    • 横切和纵切是两个相互独立的过程
    • 分别找到横切纵切可以得到的最长边长,其乘积就是可以得到的最大面积
  • 解题代码:


class Solution {
public:
    int maxArea(int h, int w, vector<int>& horizontalCuts, vector<int>& verticalCuts) {
        const int MOD = 1e9+7;
        long long maxWidth = 0;
        long long pre = 0;
       
        sort(verticalCuts.begin(), verticalCuts.end());
        for(auto verticalCut : verticalCuts)
        {    
            maxWidth = max(maxWidth, verticalCut - pre);
            pre = verticalCut;
        }
        maxWidth = max(maxWidth, w-pre);
        sort(horizontalCuts.begin(), horizontalCuts.end());
        long long maxHeight = 0;
        pre = 0;
        for(auto horizontalCut : horizontalCuts)
        {
            maxHeight = max(maxHeight, horizontalCut - pre);
            pre = horizontalCut;
        }
        maxHeight = max(maxHeight, h - pre);
        return (maxWidth * maxHeight) % MOD;
    }
};

2023.10.28

2558. 从数量最多的堆中取走礼物

  • 题目链接

  • 解题代码:

    • 用一个优先队列维护有最多礼物数量的堆
    • 每次都弹出队首元素,选走礼物,更新礼物数量,再将其入队
  • 解题代码:

class Solution {
public:
    long long pickGifts(vector<int>& gifts, int k) {
        long long ans = 0;
        priority_queue<int> pq;
        for(auto gift : gifts)
        {
            ans += gift;
            pq.push(gift);
        }
        while(k--)
        {
            int temp = pq.top();
            pq.pop();
            int take = sqrt(temp);
            ans -= temp - take;
            pq.push(take);
        }
        return ans;
    }
};

2023.10.29

LC274 H指数

  • 题目链接

  • 解题思路:

    • 二分答案 + O(n)判断答案是否合法
    • 二分枚举h指数mid,判断citations数组中是否满足有大于mid篇论文满足引用次数大于等于mid
  • 解题代码:

class Solution {
public:
    int hIndex(vector<int>& citations) {
        int n = citations.size();
        int l = 0, r = n;
        auto check = [&](int m) -> bool{
            int cnt = 0;
            for(int i = 0; i < n; i++)
            {
                if(citations[i] >= m)
                    cnt += 1;
            }
            return cnt >= m;
        };
        while(l <= r)
        {
            int m = l + (r-l)/2;
            if(check(m))
                l = m + 1;
            else
                r = m - 1;
        }
        return r;
    }
};

2023.10.30

LC275 H指数II

  • 题目链接
  • 解题思路:
    • 相当于是一个二分答案
    • 二分枚举h指数mid,判断citations数组中是否存在mid篇论文引用数大于等于mid
    • 注意肯定满足有0篇论文被引用了至少0次
  • 解题代码:
class Solution {
public:
    int hIndex(vector<int>& citations) {
        //对数时间复杂度
        int n = citations.size();
        int l = 1, r = n;
        while(l <= r)
        {
            int mid = l + (r-l)/2;
            if(citations[n-mid] >= mid)
                l = mid + 1;
            else
                r = mid - 1;
        }
        return r;
    }
};

CF26B Regular Bracket Sequence

  • 题目链接

  • 解题思路:

    • 用栈模拟

    • 如果是遇到左括号,入栈,答案加1

    • 如果是遇到右括号:

      • 栈不为空,弹栈,答案加1
      • 栈为空,答案不变
    • 最后答案是答案 - 栈中元素数量

  • 解题代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    string s;
    cin >> s;
    int ans = 0;
    int cnt = 0;
    for(auto ch : s)
    {
        if(ch == '(')
        {    
            cnt += 1;
            ans += 1;
        }
        else
        {
            if(cnt == 0)
                continue;
            ans += 1;
            cnt -= 1;
        }
    }
    cout << ans - cnt << endl;
    return 0;
}

LC32 最长有效括号

  • 题目链接

  • 解题思路:

    • 与上面CF的题相比,该题是要求一个合法的最长子串

    • 从左向右遍历一次,分别计数左括号与右括号的次数:

      • 当左括号 = 右括号次数时,找到一个合法的子串,更新答案
      • 当左括号 < 右括号次数时,将左括号右括号出现次数置0
      • 当左括号 > 右括号次数时,继续向右检查
    • 如果只用上述的思路,会漏掉一种情况:(((()))

    • 因此可以再反向使用一次上述的思路,从右向左遍历一次

      • 当左括号 = 右括号次数时,找到一个合法的子串,更新答案
      • 当左括号 > 右括号次数时,将左括号右括号出现次数置0
      • 当左括号 < 右括号次数时,继续向右检查
    • 此时答案就是最终答案

  • 解题代码:

class Solution {
public:
    int longestValidParentheses(string s) {
        //这题相当于是去找子数组
        //左一次右一次
        int n = s.size();
        int l = 0, r = 0;
        int ans = 0;
        for(int i = 0; i < n; i++)
        {
            if(s[i] == '(')
                l += 1;
            else
                r += 1;
            if(r == l)
                ans = max(ans, r * 2);
            else if(r > l)
                l = r = 0;
        }
        l = r = 0;
        for(int i = n-1; i >= 0; i--)
        {
            if(s[i] == ')')
                r += 1;
            else
                l += 1;
            if(r == l)
                ans = max(ans, r * 2);
            else if(l > r)
                l = r = 0;
        }
        return ans;
    }
};

  • 解题思路2:
    • 可以用动态规划的思想来做
    • 考虑i和i-1位置的元素:
      • 如果i位置为(,最长字串必不可能以i位置结尾,dp[i] = 0
      • 如果i位置为),i-1位置为(,二者互相匹配,dp[i] = dp[i-2] + 2
      • 如果i位置为),i-1位置为),i位置应该去与以i-1位置为结尾的合法子串前的最后一个字符匹配:
        • 如果其为(,匹配成功,dp[i] = dp[i-1] + dp[i-dp[i-1]-2] + 2
        • 如果其为),匹配失败,dp[i] = 0
    • 需要注意边界的判断
  • 解题代码:
class Solution {
public:
    int longestValidParentheses(string s) {
        int n = s.size();
        //dp
        vector<int> dp(n, 0);
        //考虑为')'的位置 与其前一位置
        //')' + '(' 两者匹配
        int ans = 0;
        for(int i = 1; i < n; i++)
        {
            if(s[i] == '(')
                continue;
            if(s[i-1] == '(')//i 和 i-1 匹配,加上以i-2为末尾可以组成的最长有效括号
                dp[i] = i - 2 >= 0 ? dp[i-2] + 2 : 2;
            else//')' ')' i 和 i-1-dp[i]匹配
            {
                if(dp[i-1] == 0)
                    continue;
                int idx = i-dp[i-1];
                if(idx-1 >= 0 && s[idx-1] == '(')
                    dp[i] = idx-2 >= 0 ? dp[i-1] + dp[idx-2] + 2 : dp[i-1] + 2;
            }
            ans = max(ans, dp[i]);
        }
        return ans;
    }
};
  • 解题思路3:

    • 也可以用栈来做
  • 解题代码:


class Solution {
public:
    int longestValidParentheses(string s) {
        int n = s.size();
        int ans = 0;
        stack<int> st;
        st.push(-1);
        for(int i = 0; i < n; i++)
        {
            if(s[i] == '(')
            {        
                st.push(i);
                continue;
            }
            else
            {
                st.pop();
                if(st.empty())
                    st.push(i);
                else
                    ans = max(ans, i - st.top());
            }
        }
        return ans;
    }
};

2023.10.31

LC2003 每棵子树内缺失的最小基因值

  • 题目链接

  • 解题思路:

    • 如果树中没有一个节点的值为1,则树中所有节点缺少的最小基因值都是1

    • 树中有节点值为1,从该节点开始向上遍历

      • 将该节点及其所有子节点加入哈希表
      • 查找最小基因值
      • 下一个遍历该节点的父亲节点
    • 在从下往上走的过程中,由于上面的节点(对应的子树)一定包含下面的节点,所以下面节点的基因值集合,一定是上面节点的基因值集合的子集,所以上面节点的 ans 值一定大于等于下面节点的 ans 值。这意味着,在计算 ans[i] 时,不需要从 1 开始枚举,而是从 ans[j]开始枚举(假设从 j往上走到了 i)。这可以将时间复杂度优化至 O(n)

  • 解题代码:

class Solution {
public:
    vector<int> smallestMissingValueSubtree(vector<int>& parents, vector<int>& nums) {
        //先判断整个树中有没有为1的节点
        int n = parents.size();
        vector<int> ans(n, 1);
        auto it = find(nums.begin(), nums.end(), 1);
        if(it == nums.end())//没有
            return ans;
        //建图
        vector<vector<int>> g(n);
        for(int i = 1; i < n; i++)
            g[parents[i]].push_back(i);
       
        unordered_set<int> vis;
        function<void(int)> dfs = [&](int x)
        {
            vis.insert(nums[x]);
            for(auto y : g[x])
            {
                if(!vis.count(nums[y]))
                    dfs(y);
            }
        };
        int mex = 2;
        int node = it - nums.begin();
        while(node >= 0)
        {
            //从为1的节点开始向上遍历
            dfs(node);
            while(vis.count(mex))
                mex += 1;
            ans[node] = mex;
            node = parents[node];
        }
        return ans;
    }
};

CF1180B Nick and Array

  • 题目链接

  • 解题思路:

    • 两个正数的积 会小于 将两个正数都变成负数之后得到的积
    • 所以可以先将数组中的所有数都变成负数
    • 此时如果负数的个数是偶数,可以直接输出答案
    • 如果负数的个数是奇数,将最小的负数变成正数
  • 解题代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n = 0;
    cin >> n;
    vector<int> a(n);
    for(int i = 0; i < n; i++)
        cin >> a[i];
   
    int mn = 0;
    int idx = -1;
    for(int i = 0; i < n; i++)
    {
        if(a[i] >= 0)
            a[i] = -a[i]-1;
        if(a[i] < mn)
        {
            mn = a[i];
            idx = i;
        }
    }
    if(n & 1)
        a[idx] = -a[idx]-1;
    for(int i = 0; i < n; i++)
        cout << a[i] << " ";
    cout << endl;
    //system("pause");
    return 0;
}

2023.11.01

LC2127. 参加会议的最多员工数

  • 题目链接

  • 解题思路:

    • 基环树
    • 先用拓扑排序,找到图中存在的环
    • 如果环长大于2,桌子上只能坐环中的这些人
    • 如果环长等于2,除去环中的人外,还分别可以坐环中人向环外方向最长路径的人
      • 因此定义dfs(i, fa),用于去找从i节点开始向环外的最长路径
      • 需要注意的是,环长为2的环之间可以互相拼接
  • 解题代码:

class Solution {
public:
    int maximumInvitations(vector<int>& favorite) {
        int n = favorite.size();
        vector<vector<int>> g(n);
        vector<vector<int>> g_out(n);
        vector<int> deg(n, 0);
        for(int i = 0; i < n; i++)
        {
            g[i].push_back(favorite[i]);
            g_out[favorite[i]].push_back(i);
            deg[favorite[i]] += 1;
        }
        //拓扑排序
        queue<int> q;
        for(int i = 0; i < n; i++)
        {
            if(deg[i] == 0)
                q.push(i);
        }
        while(!q.empty())
        {
            auto x = q.front();
            q.pop();
            for(auto y : g[x])
            {
                deg[y] -= 1;
                if(deg[y] == 0)
                    q.push(y);
            }
        }
       
        //反向dfs找节点到根节点的最长路径
        function<int(int, int)> dfs = [&](int i, int fa) -> int
        {
            int ans = 1;
            for(auto y : g_out[i])
            {
                if(y != fa)
                    ans = max(ans, dfs(y, i) + 1);
            }
            return ans;
        };
       
        int ans = 0;
        //找基环 判断长度
        int lenFor2 = 0;//长度为2的环的总长度
        for(int i = 0; i < n; i++)
        {
            if(deg[i] == 0)
                continue;
            queue<int> q;
            vector<int> ring;
            q.push(i);
            deg[i] -= 1;
            ring.push_back(i);
            while(!q.empty())
            {
                auto x = q.front();
                q.pop();
                for(auto y : g[x])
                {
                    deg[y] -= 1;
                    if(deg[y] == 0)
                    {    
                        q.push(y);
                        ring.push_back(y);
                    }
                }
            }
            int len = ring.size();
            if(len > 2)
                ans = max(ans, len);
            else if(len == 2)
                lenFor2 += dfs(ring[0], ring[1]) + dfs(ring[1], ring[0]);
        }
        ans = max(ans, lenFor2);
        return ans;
    }
};
  • 优化:在求环上节点到环外的最长路径时,可以不用dfs,而是在拓扑排序的过程中进行记录

class Solution {
public:
    int maximumInvitations(vector<int>& favorite) {
        int n = favorite.size();
        vector<vector<int>> g(n);
        vector<int> deg(n, 0);
        vector<int> longestPath(n, 1);
        for(int i = 0; i < n; i++)
        {
            g[i].push_back(favorite[i]);
            deg[favorite[i]] += 1;
        }
        //拓扑排序
        queue<pair<int, int>> q;
        for(int i = 0; i < n; i++)
        {
            if(deg[i] == 0)
                q.push({i, longestPath[i]});
        }
        while(!q.empty())
        {
            auto [x, length] = q.front();
            q.pop();
            for(auto y : g[x])
            {
                deg[y] -= 1;
                longestPath[y] = max(length + 1, longestPath[y]);
                if(deg[y] == 0)
                    q.push({y, longestPath[y]});
            }
        }
       
        int ans = 0;
        //找基环 判断长度
        int lenFor2 = 0;//长度为2的环的总长度
        for(int i = 0; i < n; i++)
        {
            if(deg[i] == 0)
                continue;
            queue<int> q;
            vector<int> ring;
            q.push(i);
            deg[i] -= 1;
            ring.push_back(i);
            while(!q.empty())
            {
                auto x = q.front();
                q.pop();
                for(auto y : g[x])
                {
                    deg[y] -= 1;
                    if(deg[y] == 0)
                    {    
                        q.push(y);
                        ring.push_back(y);
                    }
                }
            }
            int len = ring.size();
            if(len > 2)
                ans = max(ans, len);
            else if(len == 2)
                lenFor2 += longestPath[ring[0]] + longestPath[ring[1]];
        }
        ans = max(ans, lenFor2);
        return ans;
    }
};


CF777C Alyona and Spreadsheet

  • 题目链接

  • 解题思路:

    • mnLine维护对于每一行,存在满足非递减子数组的最小行
    • 对于每一列数据,分别单独考虑,并更新mnLine数组
    • 对于每一个task,判断 l 与 mnLine[r] 的关系
      • 如果 l < mnLine[r] 输出No
      • 否则 输出Yes
  • 解题代码:


#include<bits/stdc++.h>
using namespace std;
int main()
{
    int n, m;
    cin >> n >> m;
    vector<vector<int>> grid(n, vector<int>(m));
    for(int i = 0; i < n; i++)
    {
        for(int j = 0; j < m; j++)
            cin >> grid[i][j];
    }
    vector<int> mnLine(n, INT_MAX);
    for(int j = 0; j < m; j++)//检查第j列
    {
        int pre = -1;
        for(int i = 0; i < n; i++)
        {
            if(pre == -1 || grid[i][j] < grid[i-1][j])
                pre = i;
            mnLine[i] = min(mnLine[i], pre);
        }
    }
    int k = 0;
    cin >> k;
    for(int i = 0; i < k; i++)
    {
        int l, r;
        cin >> l >> r;
        l -= 1;
        r -= 1;
        if(l >= mnLine[r])
            cout << "Yes" << endl;
        else
            cout << "No" << endl;
    }
    //system("pause");
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值