最大子段和以及拓展
无限制最长连续的子序列和
https://www.acwing.com/problem/content/description/1481/
dp[i]=max(dp[i-1]+a[i],a[i]);
最终结果也就是在dp数组线性扫描找出最大值
int pos=max_element(dp+1,dp+1+n)-dp;
cout<<dp[pos]<<" ";
dp[i]表示以序列中第i个数字结尾的最大子段和
转移方程思考:如果选第i-1个数,那么由dp[i-1]转移过来,如果不选第i-1个数,那么它只能自己一个人作为一个子段存在,因为必须以第i个数结尾。
在给的题目中,还要求给出具体是哪段子段和最大,也就是左端点和右端点。
在我的代码中,是先求出来最大值,然后再排序所有符合最大值的子段的,按照题目要求左端和右端尽可能的小。
但这样在空间和时间上都是不优的,应该在线性扫描的时候就去维护最大值,一旦可以更新,也就更新左右端点。
有长度限制最长连续的子序列和
类型:单调队列优化dp
随着见的题目越来越多,单调队列也用的越来越多,单调队列用于找某个连续窗口的找到某个性质最小/大值,将这个本身O(n)的过程优化成O(1)
https://www.acwing.com/solution/content/67527/
上面的链接是题解,借鉴一下,个人认为写的很清楚。
题目描述
给定一个长度为
分析
看到求一段 连续区间 的和的问题,会想到用 前缀和 进行优化,然后就是 枚举 区间的问题
暴力枚举 子区间是一种方法,但没有优化空间;因此不妨 枚举 子区间的 右端点
闫氏DP分析法
状态表示-集合
状态表示-属性
状态计算
观察这个转移方程,首先这里的
其次,
从前向后 维护一个长度不超过
那么该状态 转移方程 就可以用 单调队列 进行优化了。这就是典型的应用场景,我们需要决策在当前m个j选哪一个j使得s[j]最小,用了单调队列维护我们就能快速知道,当前状态应该从哪一个状态转移过来最优。
Code
时间复杂度:
为了避免这个 高亮BUG,单调队列 q[]
改名为 que[]
了
由于这里 f[i]
没有相互依赖的关系,最后答案是统计所有 f[i]
的最大值
故我们直接用一个变量 res
来计算
using namespace std;
typedef long long LL;
const int N = 3e5 + 10;
int n, m;
LL s[N], que[N];
int main()
{
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++ ) scanf("%lld", &s[i]), s[i] += s[i - 1];
//处理前缀和
LL res = -1e18;
int hh = 0, tt = 0; que[0] = 0;
for (int i = 1; i <= n; i ++ )
{
while (hh <= tt && i - que[hh] > m) hh ++ ;
res = max(res, s[i] - s[que[hh]]);
while (hh <= tt && s[que[tt]] >= s[i]) tt -- ;
que[ ++ tt] = i;
}
printf("%lld\n", res);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现