C++-序列 解题思路

洛谷题目链接:P1115 最大子段和 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

【Horn Studio】编程专栏: 最大子段和 解题思路

题目

题目描述

给定一个长度为nn的序列,要求将其分成mm段序列,每一段连续,并使得每一段和的最大值尽可能小。

输入

第一行两个整数n,mn,m。

第二行nn个整数,代表序列。

输出

一行一个整数,代表每一段和的最大值的最小值。

样例输入 

5 3
4 2 4 5 1

样例输出 

6

提示

1mn1051≤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:我&&*…**¥()¥*+——}“:”?《

 

 

posted @ 2022-05-07 21:56  冯子坤  阅读(76)  评论(0编辑  收藏  举报