11/13 307. 区域和检索 - 数组可修改
给你一个数组 nums ,请你完成两类查询。
更新数组nums下标对应的值;
返回数组nums索引left与right之间元素的和。
题目链接
单点修改,区间查询,线段树/树状数组的专精领域。
线段树/树状数组的选择:
- 简单求区间和,用「前缀和」
- 多次将某个区间变成同一个数,用「线段树」
- 其他情况,用「树状数组」
树状数组的板子为:
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 次,最大化你的得分:
- 从 nums 中选择一个元素 m 。
- 将选中的元素 m 从数组中删除。
- 将新元素 m + 1 添加到数组中。
- 你的得分增加 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;
}
};