Leetcode刷题记录 2023/11/13——2023/11/19

11/13 307. 区域和检索 - 数组可修改

给你一个数组 nums ,请你完成两类查询。
更新数组nums下标对应的值;
返回数组nums索引left与right之间元素的和。

题目链接
单点修改,区间查询,线段树/树状数组的专精领域。
线段树/树状数组的选择:

  1. 简单求区间和,用「前缀和」
  2. 多次将某个区间变成同一个数,用「线段树」
  3. 其他情况,用「树状数组」

树状数组的板子为:

    int tree[30005];
    inline int lowbit(int x){return x&(-x);}
    int Query(int x){//查询前缀和
        int ans = 0;
        while(x){
            ans += tree[x];
            x -= lowbit(x);
        }
        return ans;
    }
    void Update(int x, int val){//数组中X的位置增加val
        while(x<=n){
            tree[x] += val;
            x += lowbit(x);
        }
    }

单点将下标值从nums[i]更新为新值val时,应该Update(i, val-nums[i])
查询区间时,则用前缀和求差Query(r) - Query(l-1)
注意,树状数组的下标从1开始(以便于用二进制操作)。

11/14 1334. 阈值距离内邻居最少的城市

有 n 个城市,按从 0 到 n-1 编号。给你一个边数组 edges,其中 e d g e s [ i ] = [ f r o m i , t o i , w e i g h t i ] edges[i] = [from_i, to_i, weight_i] edges[i]=[fromi,toi,weighti] 代表 f r o m i from_i fromi t o i to_i toi 两个城市之间的双向加权边,距离阈值是一个整数 distanceThreshold。
返回能通过某些路径到达其他城市数目最少、且路径距离最大为 distanceThreshold 的城市。如果有多个这样的城市,则返回编号最大的城市。

题目链接
本质是找每个点到其他点的最短距离,并获取其中距离小于distanceThreshold的数目进行比较。因此,需要求出每个点到其他点的距离。
经典最短路算法为Floyed/Dijkstra(堆优化)。

	vector<int> Dijkstra(int x,int n)//堆优化dijkstra
    {
        vector<int> dis(n, 0x3fffffff);
        vector<bool> vis(n, 0);
        dis[x] = 0;
        priority_queue<pair<int,int>, vector<pair<int,int>>, greater<pair<int,int>>> q;//用距离作为排序关键字
        q.push({0,x});
        while(!q.empty()){
            auto [_, y] = q.top();q.pop();
            if(vis[y]) continue;
            vis[y] = 1;
            for(int i=0;i<n;i++){
                if(map[y][i] == 0x3fffffff) continue;
                if(dis[i] <= dis[y] + map[y][i]) continue;
                dis[i] = dis[y] + map[y][i];//以y作为中转点更新i
                q.push({dis[i], i});//i加入队列
            }
        }
        return dis;
    }
    //floyed
    for(int k=0;k<n;k++){
    	map[k][k] = 0;
        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++)
                map[i][j] = min(map[i][j], map[i][k]+map[k][j]);//同样是用中间点更新
    }

11/15 2656. K 个元素的最大和

给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。你需要执行以下操作 k 次,最大化你的得分:

  1. 从 nums 中选择一个元素 m 。
  2. 将选中的元素 m 从数组中删除。
  3. 将新元素 m + 1 添加到数组中。
  4. 你的得分增加 m 。

题目链接
由题目显然得到:最大化得分的方式就是连续取K次数组最大值。

class Solution {
public:
    int maximizeSum(vector<int>& nums, int k) {
        int ans = nums[0];
        for(int i=1;i<nums.size();i++) ans = max(ans, nums[i]);
        return (ans+ans+k-1)*k/2;
    }
};

11/16 2760. 最长奇偶子数组

给你一个下标从 0 开始的整数数组 nums 和一个整数 threshold。 请你从 nums 的子数组中找出以下标 l 开头、下标 r
结尾 (0 <= l <= r < nums.length) 且满足以下条件的最长子数组长度:

  • nums[l] % 2 == 0
  • 对于范围 [l, r - 1] 内的所有下标 i ,nums[i] % 2 != nums[i + 1] % 2
  • 对于范围 [l, r] 内的所有下标 i ,nums[i] <= threshold

题目链接
遍历nums数组,对每个满足nums[i]%2==0的元素,尝试以其作为l,在满足条件的情况下能得到多长的最长子数组。

class Solution {
public:
    int longestAlternatingSubarray(vector<int>& nums, int threshold) {
        int length = 0, i = 0, n = nums.size();
        while(i < n){
            if(nums[i] > threshold || nums[i] % 2) i++;
            else{
                int len = 1;
                while(i+1 < n && (nums[i]%2 != nums[i+1]%2) && (nums[i+1] <= threshold)) i++, len++;
                length = max(len, length);
                i++;
            }
        }
        return length;
    }
};

11/17 2736. 最大和查询

给定两个数组num1, num2,并给出一个询问数组queries。
对queries数组内的每个二元组 [ x i , y i ] [x_i, y_i] [xi,yi],找出一个满足 n u m 1 [ j ] > x i num1[j]>x_i num1[j]>xi n u m 2 [ j ] > y i num2[j]>y_i num2[j]>yi且使 n u m 1 [ j ] + n u m 2 [ j ] num1[j]+num2[j] num1[j]+num2[j]最大的 j j j,返回 n u m 1 [ j ] + n u m 2 [ j ] num1[j]+num2[j] num1[j]+num2[j]

题目链接
Hard题。思路很清晰,考虑题干不难发现,两个数组同下标的元素永远一起考虑,不妨放入同一个容器中作为pair。
之后考虑每个询问需要满足的条件,发现需要找两个关键字均大于给定query的数,且在此基础上数越大越好。
假设我们固定第一维,则我们只需要把属于该第一维的最大第二维纳入计算即可。(最大不满足条件则全部不满足,更小的满足条件则和没有最大的大)
把num数组按照第一维排序,筛选第二维,之后对每个询问在数组上二分即可。
也可以对询问做下标排序,使其询问顺序按第一维递减顺序,这样可以使用双指针遍历。

class Solution {
public:
    vector<int> maximumSumQueries(vector<int>& nums1, vector<int>& nums2, vector<vector<int>>& queries) {
        int n = nums1.size(), m = queries.size();
        vector<int> ans(m);
        vector<pair<int, int>> pairs(n);
        for(int i=0;i<n;i++){
            pairs[i] = {nums1[i], nums2[i]};
        }
        sort(pairs.begin(), pairs.end(),
             [](auto &a, auto &b){return a.first > b.first;});
        vector<int> qid(m);
        iota(qid.begin(), qid.end(), 0);
        sort(qid.begin(), qid.end(),
             [&](int a, int b){return queries[a][0] > queries[b][0];});
        int i = 0;
        vector<pair<int, int>> st;
        for(int id: qid){
            int x = queries[id][0], y = queries[id][1];
            for(;i < n && pairs[i].first >= x;i++){
                while(!st.empty() && st.back().second <= pairs[i].first + pairs[i].second) st.pop_back();
                if(st.empty() || st.back().first < pairs[i].second) 
                    st.emplace_back(pairs[i].second, pairs[i].first+pairs[i].second);
            }
            auto it = lower_bound(st.begin(), st.end(), y, [](const auto &p, int val) { return p.first < val; });
            ans[id] = it != st.end() ? it->second : -1;
        }
        return ans;
    }
};

11/18 2342. 数位和相等数对的最大和

给你一个下标从 0 开始的数组 nums ,数组中的元素都是正整数。选出两个下标 i 和 j(i != j),且 nums[i] 的数位和与 nums[j] 的数位和相等。找出并返回 nums[i] + nums[j] 可以得到的最大值。

题目链接
哈希保存每种数位和对应的nums[i]最大值,每次计算一个数的数位和后,先与目前数位和对应最大值做加法算ans,再尝试更新最大值。

class Solution {
public:
    int maximumSum(vector<int>& nums) {
        int ans = -1;
        map<int, int> maxnum;
        for(int i=0;i<nums.size();i++){
            string cur = to_string(nums[i]);
            int count = 0;
            for(int j=0;j<cur.length();j++) count+=(cur[j]-'0');
            if(maxnum.count(count)){
                ans = max(ans, maxnum[count] + nums[i]);
                maxnum[count] = max(maxnum[count], nums[i]);
            }
            else maxnum[count] = nums[i];
        }
        return ans;
    }
};

11/19 689. 三个无重叠子数组的最大和

给定一个数组nums和长度值k,求在nums上3段互不重叠且长度为k的子数组的最大和,返回这个和。

题目链接
一道滑动窗口的好题。
三个窗口长度给定,且要求互不重叠,不妨我们就放三个窗口[0,k-1], [k, 2k-1], [2k, 3k-1],令它们自左向右滑动。
滑动的过程中,我们需要记录:

  • 第一个窗口在滑动过程中得到的最大值 m a x s u m 1 maxsum1 maxsum1与窗口末尾位置 m a x p o s 1 maxpos1 maxpos1
  • 第二个窗口在滑动过程中与第一个窗口记录最大值的求和 m a x s u m 12 maxsum12 maxsum12与该值对应两个窗口所在的末尾位置 m a x p o s 1 2 1 , m a x p o s 1 2 2 maxpos12_1, maxpos12_2 maxpos121,maxpos122(易证在此情况下第一二窗口一定不重叠)
  • 第三个窗口在滑动过程中与 m a x s u m 12 maxsum12 maxsum12的和的最大值与三个窗口末尾位置。
class Solution {
public:
    vector<int> maxSumOfThreeSubarrays(vector<int>& nums, int k) {
        //三倍滑动窗口,大小为k
        //0 to k-1
        //k to 2k-1
        //2k to 3k-1
        vector<int> ans;
        int sum1=0, sum2=0, sum3=0;
        int maxsum1=0, maxsum1_idx=0, maxsum12=0, maxsum12_idx1=0,maxsum12_idx2=0;
        int maxsum=0;
        for(int i=2*k; i<nums.size(); i++){
            sum1 += nums[i-2*k];
            sum2 += nums[i-k];
            sum3 += nums[i];
            if(i >= 3*k-1){
                if(sum1 > maxsum1){
                    maxsum1 = sum1;
                    maxsum1_idx = i - 3*k + 1;
                }//第一段
                if(maxsum1 + sum2 > maxsum12){
                    maxsum12 = maxsum1 + sum2;
                    maxsum12_idx1 = maxsum1_idx;
                    maxsum12_idx2 = i - 2*k + 1;
                }//第二段
                if(maxsum12 + sum3 > maxsum){
                    maxsum = maxsum12 + sum3;
                    ans = {maxsum12_idx1, maxsum12_idx2, i-k+1};
                }//第三段
                sum1 -= nums[i-3*k+1];
                sum2 -= nums[i-2*k+1];
                sum3 -= nums[i-k+1];
            }
        }
        return ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值