划分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;

}

}

相关推荐
vibecoding日记4 小时前
双非如何快速入职字节等大厂大模型?真实案例分析:推理优化和投机解码
算法·求职·大模型工程师
yszaygr21386 小时前
Verilog参数化游程编码RLE模块
算法
望易6 小时前
刚设计的大模型架构-双域耦合认知框架
算法·架构
复杂网络10 小时前
多个 Claude Code 与多个 Codex 协同工作:设计与实现方案
算法
HjhIron1 天前
面试常客:字符串算法从入门到进阶
算法·面试
吴佳浩1 天前
DeepSeek DSpark:Confidence-Scheduled Speculative Decoding 技术解析
人工智能·算法·deepseek
触底反弹1 天前
🧠 搞懂 Token,才算真正入门大模型——从分词原理到 Embedding 语义实战
javascript·人工智能·算法
vivo互联网技术1 天前
ICLR 2026 | 基于后验采样的图像恢复方法LearnIR:人脸去阴影、去雾
人工智能·算法·aigc
浮生望1 天前
JS字符串与回文算法:从包装类到双指针的面试进阶之路
javascript·算法
黄敬峰1 天前
面试必刷:从JS底层包装类到双指针,彻底搞懂字符串与回文算法
算法