LeetCode第45场双周赛-解题报告

LeetCode第45场双周赛-解题报告

A. 唯一元素的和

原题链接

https://leetcode-cn.com/problems/sum-of-unique-elements/
在这里插入图片描述
在这里插入图片描述

解题思路

因为数据范围比较小,可以直接模拟,如果出现一次就加上去。
或者是直接map打表也可以

AC代码

暴力

class Solution {
public:
    bool Check(int x, vector<int> &nums){
        int cnt = 0;
        for (auto y : nums)
            if (x == y)
                cnt ++;
        return cnt == 1;
    }
    int sumOfUnique(vector<int>& nums) {
        int ret = 0;
        for (auto x : nums)
        {
            if (Check(x, nums))
                ret += x;
        }
        return ret;
    }
};

打表

class Solution {
public:
    int sumOfUnique(vector<int>& nums) {
        int ans = 0;
        unordered_map<int, int> m;
        for (auto x : nums)
            m[x] ++;

        for (auto [u, v] : m)   // 注意这个用法
            if (v == 1)
                ans += u;
        
        return ans;
    }
};

B. 任意子数组和的绝对值的最大值

原题链接

https://leetcode-cn.com/problems/maximum-absolute-sum-of-any-subarray/
在这里插入图片描述

解题思路

  • 解题主要从子串入手,求得的是区间和的绝对值,那么很可能会用到前缀和
  • 进一步分析以 R i R_i Ri为结尾子数组和绝对值最大时, L i L_i Li会有什么性质, m a x ( a b s ( P r e S u m R i − P r e S u m L i − 1 ) ) max(abs(PreSum_{R_i}- PreSum_{L_i-1})) max(abs(PreSumRiPreSumLi1)), P r e S u m R i PreSum_{R_i} PreSumRi是确定的,根据绝对值的性质,可知,最大值取出在 P r e S u m L i − 1 PreSum_{L_i-1} PreSumLi1 P r e S u m R i PreSum_{R_i} PreSumRi左侧最小,或 P r e S u m R i PreSum_{R_i} PreSumRi右侧最大,那么我直接保留出 P r e M a x , P r e M i n PreMax,PreMin PreMax,PreMin就可以线性处理出问题。

AC代码

class Solution {
public:
    int maxAbsoluteSum(vector<int>& nums) {
        int n = nums.size();
        vector<int> pre(n + 5);
        pre[0] = 0;
        for (int i = 1; i <= n; i ++ )  pre[i] = pre[i - 1] + nums[i - 1];
        
        int pre_max = 0, pre_min = 0;
        int res = 0;
        for (int i = 1; i <= n; i ++ )  // 对于右端点进行枚举
        {
            pre_max = max(pre_max, pre[i]); pre_min = min(pre_min, pre[i]);
            res = max(res, max(abs(pre[i] - pre_max), abs(pre[i] - pre_min)));
        }
        return res;
    }
};

C. 删除字符串两端相同字符后的最短长度

原题链接

https://leetcode-cn.com/problems/minimum-length-of-string-after-deleting-similar-ends/
在这里插入图片描述
在这里插入图片描述

解题思路

  • 1.当两端字符不相同时,无法再次缩短
  • 2.当两端字符相同时,那么一定会取完
    • 倘若一次尚未全部取完,那么可能会导致左右两侧字符不等,形成1.的局面

AC代码

class Solution {
public:
    int minimumLength(string s) {
        int pre = 0, nxt = s.size() - 1;
        char ch;
        while (s[pre] == s[nxt] && pre < nxt)  // 倘若只剩最后一个,是不行的
        {
            ch = s[pre];
            while (s[pre] == ch && pre <= nxt)   pre ++;    // 注意是可以等于的
            while (s[nxt] == ch && pre <= nxt)   nxt --;
        }
        return nxt - pre + 1;
    }
};

D. 最多可以参加的会议数目 II

原题链接

https://leetcode-cn.com/problems/maximum-number-of-events-that-can-be-attended-ii/
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

解题思路

倘若所有会议价值相同,仅仅有区间冲突,那么这就是一个贪心问题
只进行右端点排序,然后进行线性扫一遍数组,能加则加,因为我们是取最大数量的同时,使得结束时间尽可能的早。
当价值不同是,那么这是一个dp问题,思路类似于背包思路
在这里插入图片描述

AC代码

const int N = 1000010;
class Node
{
public:
    int st, ed, value;
    Node(int _st = 0, int _ed = 0, int _value = 0)
    {
        st = _st, ed = _ed, value = _value;
    }
}q[N];
bool cmp(const Node &t1, const Node &t2)
{
    if (t1.ed == t2.ed)
        return t1.st < t2.st;
    else
        return t1.ed < t2.ed;
}

class Solution {
public:
    int maxValue(vector<vector<int>>& events, int k) {
        int n = events.size();
        for (int i = 0; i < n; i ++ )
            q[i + 1] = Node(events[i][0], events[i][1], events[i][2]);
        sort(q + 1, q + 1 + n, cmp); // 排序

        // vector<vector<int>(k + 5) > f(n + 5);   // dp 数组
        vector<vector<int>> f(n + 5, vector<int>(k + 5));

        vector<int> pre(n + 5); // 预处理二分最近的不冲突区间
        
        
        pre[1] = 0;     // 注意这个是0,而不是 - 1
        for (int i = 2; i <= n; i ++ )
        {
            static int l, r, mid, x;
            x = q[i].st;
            l = 1, r = i - 1;
            // q[mid].ed < x 的最大值
            if (q[1].ed >= x)
            {
                pre[i] = 0;
                continue;
            }
            while (l < r)
            {
                mid = (l + r + 1) >> 1;
                if (q[mid].ed < x)
                    l = mid;
                else
                    r = mid - 1;
            }
            pre[i] = l;
        }

        // 进行dp 初始化,按道理说没有也是行的
        for (int i = 0; i <= k; i ++ )
            f[0][i] = 0;
        /*
        for (int i = 1; i <= n; i ++ )
            f[i][0] = 0, f[i][1] = max(q[i].value, f[i-1][1]);  // 这个地方容易写错,不如在下面初始化
        */
        for (int i = 1; i <= n; i ++ )
        {
            for (int j = 1; j <= k; j ++ )
            {
                f[i][j] = f[i - 1][j];  // 不选 i 
                /// if (pre[i] == -1)   continue;
                f[i][j] = max(f[i][j], f[pre[i]][j - 1] + q[i].value);  // 选了 i 
            }
        }        


        return f[n][k];
    }
};

心得

  • auto [u, v] : m 很好使,简洁高效
  • 处理子串(连续的)问题时,常常需要预处理区间,然后进行贪心,类似dp求解
  • vector<vector> f(n + 5, vector(k + 5)); 有趣的写法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值