nyoj 44 子串和【最大子串和】

来源:互联网 发布:在线视频转码 知乎 编辑:程序博客网 时间:2024/05/17 02:15

子串和

时间限制:5000 ms  |  内存限制:65535 KB
难度:3
描述
给定一整型数列{a1,a2...,an},找出连续非空子串{ax,ax+1,...,ay},使得该子序列的和最大,其中,1<=x<=y<=n。
输入
第一行是一个整数N(N<=10)表示测试数据的组数)
每组测试数据的第一行是一个整数n表示序列中共有n个整数,随后的一行里有n个整数I(-100=<I<=100),表示数列中的所有元素。(0<n<=1000000)
输出
对于每组测试数据输出和最大的连续子串的和。
样例输入
151 2 -1 3 -2
样例输出
5
提示
输入数据很多,推荐使用scanf进行输入


最大子串和,用这样动态更新的方法来做,比较省时间,谈谈个人对这个方法的理解:

无论给出的序列是什么样的,他的最大子串和肯定是存在的,所以现在的出发点就基于此,一个变量 tp 统计每次增加一个元素的值能达到的数值,另一个 sum 来记录最大值,每加入一个元素,tp 改变,比较 tp 是否超过了现在的最大值 sum 如果超过了,那么就更新 sum 为 tp的值,因为现在最大的值是 tp 的值了,然后来讨论 tp 的更新处理,比较容易想到,如果本来前 n 项的和(从某一个起点开始的tp 的值)还是正的,结果多加了后一项之后,突然这个 tp 变成负的了,那么证明这一项是个非常大的负数,那么最大的子串和的序列肯定不包含这一个元素,然后继续求子串和,就直接从后一项开始了.把刚才那一项跳过,就这样更新下去,最终求得的肯定是最大子串的和,因为我们只在当前子串和 tp 比 sum 大的时候进行更新,否则不更新,而 tp 这个变量,可以把所有可能达到最大和的子串的和都求了一遍,所以最大的和一定能记录下来,只是,这个子串并没有记录下来,只记录了这个最大的值而已,如果想记录这个序列,估计还要再想解决办法.....自己没理解透彻,暂时不赘述了.....

注意这个题的坑,初始化的时候不能初始化为 0 ,要初始化为一个最小值........


 #include<stdio.h>#define maxn 1000005int x[maxn],n;void solve(){int tp,sum;tp=0;sum=-maxn;for(int i=0;i<n;++i){tp+=x[i];//累加if(tp>sum)//比记录的值大{sum=tp;//更新}else if(tp<0)//小于零,就重新开始{tp=0;}}printf("%d\n",sum);}int main(){int t;scanf("%d",&t);while(t--){scanf("%d",&n);for(int i=0;i<n;++i){scanf("%d",x+i);}solve();}return 0;}                        

2016年4月24日8:59


今天用着递推的方法推导了一下,感觉好神奇


dp[i] 表示以第i个元素结尾的子串的最大和的值

那么dp[i]的来源可能是dp[i-1]+x[i](i项一直累加),或者x[i](可能前前i-1项和还没有第i项大)

然后 状态转移就是 dp[i]=max(dp[i-1]+x[i],x[i])



#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn=1000005;int x[maxn],dp[maxn];int maxsum(int n){memset(dp,0,sizeof(dp));int ans=dp[0]=x[0];for(int i=1;i<n;++i){dp[i]=max(dp[i-1]+x[i],x[i]);ans=max(ans,dp[i]);}return ans;}int main(){int t;scanf("%d",&t);while(t--){int n;scanf("%d",&n);for(int i=0;i<n;++i){scanf("%d",&x[i]);}printf("%d\n",maxsum(n));}return 0;}


0 0
原创粉丝点击