Max Sum(第一周J题)

来源:互联网 发布:python genetic 编辑:程序博客网 时间:2024/04/30 23:08

Description

Given a sequence a[1],a[2],a[3]......a[n], your job is to calculate the max sum of a sub-sequence. For example, given (6,-1,5,4,-7), the max sum in this sequence is 6 + (-1) + 5 + 4 = 14. 

Input

The first line of the input contains an integer T(1<=T<=20) which means the number of test cases. Then T lines follow, each line starts with a number N(1<=N<=100000), then N integers followed(all the integers are between -1000 and 1000). 

Output

For each test case, you should output two lines. The first line is "Case #:", # means the number of the test case. The second line contains three integers, the Max Sum in the sequence, the start position of the sub-sequence, the end position of the sub-sequence. If there are more than one result, output the first one. Output a blank line between two cases. 

Sample Input

25 6 -1 5 4 -77 0 6 -1 1 -6 7 -5

Sample Output

Case 1:14 1 4Case 2:7 1 6

先不说如何解题,重点是先审题!看准输出格式要求,两个例子间换行,也就是最后一个例子后不用换行,因为这个PE了几次特别不值。

解法转自:

最大子序列和问题中的算法四

 算法四:

      下面介绍一个线性的算法,这个算法是许多聪明算法的典型:运行时间是明显的,但是正确性则很不明显(不容易理解)。

      //线性的算法O(N) 

      long maxSubSum4(const vector<int>& a) 

      { 

      long maxSum = 0, thisSum = 0; 

      for (int j = 0; j < a.size(); j++) 

      { 

      thisSum += a[j]; 

      if (thisSum > maxSum) 

      maxSum = thisSum; 

      elseif (thisSum < 0) 

      thisSum = 0; 

      } 

      return maxSum; 

      }

      很容易理解时间界O(N)是正确的,但是要是弄明白为什么正确就比较费力了。其实这个是算法二的一个改进。分析的时候也是i代表当前序列的起点,j代表当前序列的终点。如果我们不需要知道最佳子序列的位置,那么i就可以优化掉。

      重点的一个思想是:如果a[i]是负数那么它不可能代表最有序列的起点,因为任何包含a[i]的作为起点的子序列都可以通过用a[i+1]作为起点来改进。类似的有,任何的负的子序列不可能是最优子序列的前缀。例如说,循环中我们检测到从a[i]到a[j]的子序列是负数,那么我们就可以推进i。关键的结论是我们不仅可以把i推进到i+1,而且我们实际可以把它一直推进到j+1。

      举例来说,令p是i+1到j之间的任何一个下标,由于前面假设了a[i]+…+a[j]是负数,则开始于下标p的任意子序列都不会大于在下标i并且包含从a[i]到a[p-1]的子序列对应的子序列(j是使得从下标i开始成为负数的第一个下标)。因此,把i推进到j+1是安全的,不会错过最优解。注意的是:虽然,如果有以a[j]结尾的某序列和是负数就表明了这个序列中的任何一个数不可能是与a[j]后面的数形成的最大子序列的开头,但是并不表明a[j]前面的某个序列就不是最大序列,也就是说不能确定最大子序列在a[j]前还是a[j]后,即最大子序列位置不能求出。但是能确保maxSum的值是当前最大的子序列和。

对上面的算法进行改进后就可以记录子序列的最后一个下标,但开始的下标依然不好记录,这里有两种办法

1).顺序求一遍最大和子序列后逆序在求一遍,即可求得子序列开始下标。

2).记录子序列长度(代码注释部分),每次更新子序列尾结点的时候更新长度,注意这里不能直接使用所记录的子序列开始的下标,因为有可能子序列开始下标更新了而结束下标未更新(例 5 0 0 0 -10)。

这种算法的缺点就是要特殊考虑数列全为负数的情况。

<span style="font-family:FangSong_GB2312;">#include <iostream>#include <cstdio>#include <cstring>#define M 100005int num[M];using namespace std;int main(){int t;scanf("%d",&t);int cnt=0;int f=1;while(t--){cnt++;int n;scanf("%d",&n);int max=-1001,t;for(int i=0;i<n;i++){scanf("%d",&num[i]);if(num[i]>max){max=num[i];t=i;}}if(max<0)//判断数列是否全为负数 {if(f)printf("Case %d:\n%d %d %d\n",cnt,max,t+1,t+1);elseprintf("\nCase %d:\n%d %d %d\n",cnt,max,t+1,t+1);f=0;continue;}long long sum=0,ans=0;int s=0,e=0;//int b=0,l=0; for(int i=0;i<n;i++)//正向查找 {sum+=num[i];if(sum>=ans){e=i;ans=sum;//l=e-b;}else if(sum<0){sum=0;//b=i;这种记录长度的方法输出下标分别为e-l+1,e+1; }}sum=0;for(int i=n-1;i>=0;i--)//逆向查找 {sum+=num[i];if(sum>=ans){s=i;ans=sum;}else if(sum<0){sum=0;}}if(f){printf("Case %d:\n%lld %d %d\n",cnt,ans,s+1,e+1);//一定要注意输出格式 f=0;}elseprintf("\nCase %d:\n%lld %d %d\n",cnt,ans,s+1,e+1);}return 0;}</span>



0 0
原创粉丝点击