C++-序列 解题思路
洛谷题目链接:P1115 最大子段和 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
【Horn Studio】编程专栏: 最大子段和 解题思路
题目
题目描述
给定一个长度为nn的序列,要求将其分成mm段序列,每一段连续,并使得每一段和的最大值尽可能小。
输入
第一行两个整数n,mn,m。
第二行nn个整数,代表序列。
输出
一行一个整数,代表每一段和的最大值的最小值。
样例输入
5 3
4 2 4 5 1
样例输出
6
提示
1≤m≤n≤1051≤m≤n≤105,序列和不超过109109
来源
思路
这道题用的办法多的去了!可以使用二分,dp,区间,线段树,枚举(复杂度n^3),字符串……基本上所有办法都可以解决。
贪心思路
是用一个sum记录当前前缀和,一路累积过去,如果前缀和sum变成了负数,那么下一个数就不需要前面的数了(因为还不如只选它一个),这时把sum置为0,再继续累加
枚举思路
爆搜这代码简单直白。实际上我们需要枚举l和r,然后计算[l,r]的元素和就阔以了。
DP
第一个数为一个有效序列,如果一个数加上上一个有效序列得到的结果比这个数大,那么该数也属于这个有效序列。如果一个数加上上一个有效序列得到的结果比这个数大,那么该数也属于这个有效序列。
如果一个数加上上一个有效序列得到的结果比这个数小,那么这个数单独成为一个新的有效序列.
在处理的过程中计算有效序列的所有元素之和。
最后取最大值即可。
二分
如果嫌麻烦可以遏制二分次数,大概30次就是1e9的水平了,2^30=10|7374|1824,我们一个个列举,代码如下:
for(int j=1,k=1;j<=m;j++) { for(int t=0;t+a[k]<=ans+(1<<i)&&k<=n;k++) t+=a[k]; if(k>n) goto Next; }
goto语句的作用:
可能有一点难用及陈余,但是在某些场合还是有点用的!
代码 贪心
感谢Robert提供代码,Robert 的个人中心 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
#include<bits/stdc++.h>
using namespace std; int n,j,sum,maxx;
int main(){ scanf("%d%d",&n,&maxx);
sum=maxx; while(--n){
scanf("%d",&j);
sum=sum>0?sum:0;
sum+=j;
maxx=maxx>sum?maxx:sum;
} return (printf("%d",maxx))&0;//输出并return 0 }
代码 DP
#include<bits/stdc++.h> using namespace std; int n,a[200020],b[200020],i,ans=-2147483647; int main(){ cin>>n; for(i=1;i<=n;i++){ cin>>a[i]; if(i<2) b[i]=a[i]; else b[i]=max(a[i],b[i-1]+a[i]); ans=max(ans,b[i]); } cout<<ans; return 0; }
代码 二分
#include<bits/stdc++.h> using namespace std; int n,m,ans,a[100005]; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",a+i); for(int i=30;i>=0;i--) { for(int j=1,k=1;j<=m;j++) { for(int t=0;t+a[k]<=ans+(1<<i)&&k<=n;k++) t+=a[k]; if(k>n) goto Next; } ans+=1<<i; Next:; } printf("%d\n",ans+1); }
彩蛋
xxx:哎我编译器崩溃了,写不了代码了!哈哈哈
冯子坤:来来来C++ Shell (cpp.sh)这个在线编译器给你,很好用
xxx:我&&*…**¥()¥*+——}“:”?《