划分dp+滑窗+前缀和|deque优化

lc3310

dfs标记可疑方法

入度+连通块

class Solution {

public:

vector<int> remainingMethods(int n, int k, vector<vector<int>>& invocations)

{

vector<vector<int>> g(n);

for (auto& e : invocations)

++ge\[0].push_back(e1);++

// 标记所有可疑方法

vector<int> is_suspicious(n);

auto dfs = \&(auto&& dfs, int x) -> void {

is_suspiciousx = true;

++for (int y : gx) {++

if (!is_suspiciousy) { // 避免无限递归

dfs(dfs, y);

}

}

};

dfs(dfs, k);

++// 检查是否有【非可疑方法】->【可疑方法】的边
for (auto& e : invocations) {
if (!is_suspiciouse\[0] && is_suspiciouse\[1]) {
// 无法移除可疑方法
vector<int> ans(n);
iota(ans.begin(), ans.end(), 0);
return ans;
}
}
++

// 移除所有可疑方法

vector<int> ans;

for (int i = 0; i < n; i++) {

if (!is_suspiciousi) {

ans.push_back(i);

}

}

return ans;

}

};

lc3578

其实是很典型 单调dq优化线性dp

半月前应该也刷到了一道类似的,第一时间还是没优化出来orz哭哭哭

class Solution {

public:

const static int N=5e4+10;

int fN;

int countPartitions(vector<int>& nums, int k) {

int n=nums.size();

int mod=1e9+7;

f0=1;

deque<int> qx,qn;

int sum=0,l=0;

for(int i=0;i<n;i++){

while(!qx.empty()&&numsi>=numsqx.back()) qx.pop_back();

qx.push_back(i);

while(!qn.empty()&&numsi<=numsqn.back()) qn.pop_back();

qn.push_back(i);

sum=(sum+fi)%mod;

while(!qx.empty()&&numsqx.front()-numsqn.front()>k){

if(l==qx.front()) qx.pop_front();

if(l==qn.front()) qn.pop_front();

sum=(sum+mod-fl)%mod;

l++;

}

fi+1=sum;

}

// for(int i=0;i<=n;i++) cout<<fi<<' ';puts("");

return fn;

}

};

deque优化划分dp

++idx 单调dq 维护窗口内的最大、最小值++,结合前缀和数组

++滑动窗口中统计元素差不超过k的合法分区数并取模++

class Solution {

public:

int countPartitions(vector<int>& nums, int k) {

const int MOD = 1'000'000'007;

int n = nums.size();

deque<int> min_q, max_q;

vector<int> f(n + 1);

f0 = 1;

long long sum_f = 0; // 窗口中的 fi 之和

int left = 0;

for (int i = 0; i < n; i++) {

int x = numsi;

++// 1. 入
sum_f += fi;
++

while (!min_q.empty() && x <= numsmin_q.back())

min_q.pop_back();

min_q.push_back(i);

while (!max_q.empty() && x >= numsmax_q.back()) {

max_q.pop_back();

}

max_q.push_back(i);

// 2. 出

++while (numsmax_q.front() - numsmin_q.front() > k) {++

++sum_f -= fleft;++

++left++;
if (min_q.front() < left)
++

min_q.pop_front();

if (max_q.front() < left)

max_q.pop_front();

}

// 3. 更新答案

++fi + 1 = sum_f % MOD;
}
++

++return fn;++

}

};

  1. 问题转化:要统计"所有元素差不超过K的子段分割方案数",可以用动态规划,设 ++fi 为前 i -1个元素的✓condition分割方案数++

  2. 滑动窗口找合法左边界:

用 multiset 维护窗口内的最大/最小值,通过双指针 j 找到每个右端点 i 对应的最小合法左边界(保证窗口内元素差≤K)

  1. 前缀和优化转移: fi 的方案数等于"前 i-1 个元素的所有合法前缀方案数之和",用前缀和++数组 g 快速计算这个区间和++ ,从而将转移时间复杂度优化到O(1)。

结合动态规划+滑动窗口+前缀和,就能高效解决子段分割计数问题啦~

class Solution {

public:

int countPartitions(vector<int>& nums, int K) {

int n = nums.size();

const int MOD = 1e9 + 7;

// fi:前 i 个元素的分割方案数

// gi:f 的前缀和

long long fn + 1, gn + 1;

f0 = g0 = 1;

// 用 multiset 记录滑动窗口里的数,方便求出最小值和最大值

multiset<int> ms;

// 枚举双指针的右端点 i,计算合法子段左端点的最小值 j

for (int i = 1, j = 1; i <= n; i++) {

ms.insert(numsi - 1);

while (j < i && *prev(ms.end()) - *ms.begin() > K) {

ms.erase(ms.find(numsj - 1));

j++;

}

// j 是最小的左端点

// 那么上一个子段最小的右端点就是 j - 1

// 前缀和就得减去 j - 2 的值

fi = (gi - 1 - (j - 2 >= 0 ? gj - 2 : 0) + MOD) % MOD;

gi = (gi - 1 + fi) % MOD;

}

return fn;

}

};

lc2762 不间断子数组

滑窗

class Solution {

public:

long long continuousSubarrays(vector<int>& nums) {

long long ans = 0;

map<int, int> cnt;

int left = 0;

for (int right = 0; right < nums.size(); right++) {

cntnums\[right]++;

while (cnt.rbegin()->first - cnt.begin()->first > 2) {

int y = numsleft;

if (--cnty == 0) {

cnt.erase(y);

}

left++;

}

ans += right - left + 1;

}

return ans;

}

};

双堆法

class Solution {

public long continuousSubarrays(int\[\] nums) {

long res=0;

int left=0;

PriorityQueue<Integer> less=new PriorityQueue<>();//最小堆

PriorityQueue<Integer> more=new PriorityQueue<>((a,b)->(b-a));//最大堆

for(int i=0;i<nums.length;i++){

less.offer(numsi);

more.offer(numsi);

while(more.peek()-less.peek()>2){

more.remove(numsleft);

less.remove(numsleft);

left++;

}

res+=i-left+1;

}

return res;

}

}

相关推荐
朝朝辞暮i9 分钟前
C++ 第 31C 课:Subscriber 到底是什么
开发语言·c++·算法
Evand J42 分钟前
【MATLAB例程】三维RRT+APF路径规划与AOA-TDOA融合定位算法。附代码的下载链接
算法·matlab·路径规划·rrt·tdoa·apf·aoa
海绵宝宝转agent1 小时前
leetCode100算法开源笔记分享
笔记·算法
写后端的胖头鱼2 小时前
时间复杂度 & 空间复杂度
java·数据结构·算法·时间复杂度·空间复杂度
hahaha60162 小时前
黑体轨迹和色温--光源白点
人工智能·嵌入式硬件·算法·计算机视觉
海绵宝宝转agent2 小时前
基于Redis ZSet+AOP+注解实现限流注解算法
数据库·redis·算法
All for pursuit.3 小时前
【回溯-7】494.目标和
数据结构·c++·算法·leetcode
白杨尚青3 小时前
C++入门篇(十三):vector(上)——动态数组:构造、空间增长与迭代器失效
开发语言·c++·笔记·算法·stl
Jasmine_llq3 小时前
《P17461 [GESP202609 八级] 生成树计数》
算法·栈·tarjan 算法·边双连通分量·深度优先搜索 dfs·tarjan 保存当前路径节点·模乘法
Jasmine_llq3 小时前
《P17462 [GESP202609 八级] 末班车》
算法·dijkstra 算法·最大化版本,求最长可行值·建反向图(逆图 / 反转边)·多源 dijkstra 思想·对每个终点单独跑一遍djst·离线预处理 + 在线查询