
《算法竞赛进阶指南》0x12 T4 最大子序和
发布日期:2021-05-04 20:14:44
浏览次数:33
分类:技术文章
本文共 2485 字,大约阅读时间需要 8 分钟。
题目描述
输入一个长度为 n n n 的整数序列,从中找出一段长度不超过 m m m 的连续子序列,使得子序列中所有数的和最大。
注意: 子序列的长度至少是 1 1 1。输入格式
第一行输入两个整数 n n n , m m m。
第二行输入 n n n 个数,代表长度为 n n n 的整数序列。 同一行数之间用空格隔开。输出格式
输出一个整数,代表该序列的最大子序和。
数据范围
1 1 1 ≤ \leq ≤ n n n , m m m ≤ \leq ≤ 300000 300000 300000
输入样例
6 4
1 -3 5 1 -2 3输出样例
7
题解
算法1:暴力枚举 O ( n m ) O(nm) O(nm)
通过两层循环枚举每一个不超过 m m m的连续子序列
code
#includeusing namespace std;const int N=3e5+10;int n,m,a[N],maxn=-1,sum=0;int main(){ cin>>n>>m; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=n-m+1;i++) //i表示以i开头的子序列 { sum=0;//枚举每个子序列前将sum初始化 for(int j=i;j<=i+m-1;j++) //j表示子序列的长度 { sum+=a[j]; maxn=max(sum,maxn); } } cout<
算法2:前缀和优化 O ( n m ) O(nm) O(nm)
利用前缀和解决算法1中的 s u m sum sum的累加过程
#includeusing namespace std;const int N=3e5+10;int n,m,a[N],s[N],maxn=-1,sum=0;int main(){ cin>>n>>m; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i];//s[i]表示a[1]+a[2]+...+a[n] for(int i=1;i<=n;i++) { int x=i-m;//x为与i距离为m的点的前一位,即最远的左端点的前一位 if(x<0) x=0;//防止下标越界 for(int j=x;j<=i-1;j++)//枚举左端点的前一位 maxn=max(maxn,s[i]-s[j]); } cout<
算法3:优先队列优化 O ( N ) O(N) O(N)
假设 s s s数组中的三个下标分别为 i , j , k ( k < j < i ) i,j,k(k<j<i) i,j,k(k<j<i)
根据算法2中的前缀和优化,在 i i i保持不变的情况下, s [ i ] s[i] s[i]也保持不变,序列的中所有数的累加和为 s [ i ] − s [ l e f t ] s[i]-s[left] s[i]−s[left],所以 s [ l e f t ] s[left] s[left]越小,序列中的数字和就越大,也就更优。 那么如果 s [ k ] ≥ s [ j ] s[k] \geq s[j] s[k]≥s[j]那 k k k一定是一个无用的点,因为相比较而言, k k k的位置以及 s s s数组中的值都不如 j j j号点,所以 k k k就没有任何价值 所以可以设计一个算法 用一个队列来模拟以下的操作,队列储存的是这个子序中每个元素的编号 从前向后遍历,如果 i i i到队首的元素编号 q [ h e a d ] q[head] q[head]超过了 m m m,就把队首往后推 如果我当前这个元素的值要比队尾的元素更优,即 s [ i ] < s [ q [ t a i l ] ] s[i]<s[q[tail]] s[i]<s[q[tail]](从前面的讲解可知,元素的价值取决于它的位置和 s s s数组中的值,因为是从前向后遍历,所以当前位置一定比队列中任何一个数的位置更优,所以只需要比较 s s s数组中的值)就将队尾的元素删去,并把这个元素塞到队尾 由此可知,当前维护的队列一定是一个具有单调性的(从队首到队尾的元素均从大到小或从小到大),因此称为单调队列 最后不断更新ans的值就可以啦由于每个点都只会进出队列一次,所以时间复杂度就是 O ( N ) O(N) O(N)
#includeusing namespace std;const int N=300010;int a[N],s[N],q[N],head=0,tail=0;int main(){ int ans=-1; int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); s[i]=s[i-1]+a[i];//前缀和 } for(int i=1;i<=n;i++) { if(i-q[head]>m) head++;//处理队首 //用if的原因是对于i来说,最多只可能把队首踹掉一次。 //其他大于i的点已经被i号点以前的点给踹掉了,也就是说被踹掉的队首一定是第i-m号点 while(s[i]<=s[q[tail]]&&head<=tail) tail--;//处理队尾 //用while的原因是队尾可能有多个大于等于s[i]的点,要将这些点都踹掉才可以 q[++tail]=i;//储存当前的点 ans=max(ans,s[i]-s[q[head]]);//更新答案 } printf("%d",ans); return 0;}
发表评论
最新留言
关注你微信了!
[***.104.42.241]2025年04月07日 03时16分45秒
关于作者

喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!
推荐文章
在 Python 调试过程中设置不中断的断点 | Linux 中国
2019-03-03
AI 系统向自动化编码迈进 | Linux 中国
2019-03-03
使用 Jupyter Notebooks 构建一个远程管理控制台 | Linux 中国
2019-03-03
使用开源可视化工具来理解你的 Python 代码 | Linux 中国
2019-03-03
【2021 ECUG Con】聚势而来,与你相约花开时
2019-03-03
硬核观察 | 有人在比特币骗局中损失了 10 个比特币
2019-03-03
FreeDOS 的简单介绍 | Linux 中国
2019-03-03
LCTT 2018:五周年纪念日 | Linux 中国
2019-03-03
使用 top 命令了解 Fedora 的内存使用情况 | Linux 中国
2019-03-03
Bat:一种具有语法高亮和 Git 集成的 Cat 类命令 | Linux 中国
2019-03-03
Linux 上最好的五款音乐播放器 | Linux 中国
2019-03-03
网易云首倡中台方法论,发布全链路中台技术方案
2019-03-03
传输层协议
2019-03-03
如何加载dll文件计算UDS服务的秘钥
2019-03-03
细数哪些网络用户需要换IP?
2019-03-03
codeforces1307D 1900分最短路
2019-03-03
2020牛客暑期多校训练营(第九场)
2019-03-03