九度OJ-题目1372:最大子向量和(连续子数组的最大和)

题目链接地址:

九度OJ-题目1372:最大子向量和(连续子数组的最大和)


题目描述:
HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天JOBDU测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。你会不会被他忽悠住?

输入:
输入有多组数据,每组测试数据包括两行。
第一行为一个整数n(0<=n<=100000),当n=0时,输入结束。接下去的一行包含n个整数(我们保证所有整数属于[-1000,1000])。

输出:
对应每个测试案例,需要输出3个整数单独一行,分别表示连续子向量的最大和、该子向量的第一个元素的下标和最后一个元素的下标。若是存在多个子向量,则输出起始元素下标最小的那个。

样例输入:
3
-1  -3  -2
5
-8  3  2  0  5
8
6  -3  -2  7  -15  1  2  2
0

样例输出:
-1  0  0
10  1  4
8  0  3


解题思路:

很久以前做过这道题,但是现在只记得这是一道经典的动态规划问题,其余的记忆都已经随风飘散了。。。对动态规划也是一知半解。于是上网搜了一下解法,以下是算法的步骤:

从头到尾扫描向量,遇到以下两种情况后进行相应的处理:
(1)如果当前子向量的和大于之前子向量的最大和,则更新连续子向量的最大和,子向量起点,子向量终点;
(2)如果当前子向量的和小于0,就将该连续子向量看成一个等于其和的负数,因为要找出具有最大和的子向量,所以子向量中尽量不要包含负数,因此可以先将该负数暂时“移出”子向量,将当前子向量和设置为0,从下一个向量元素开始继续寻找有可能具有最大和的连续子向量。
下面举个栗子,对于测试用例{ -8,-7,3,2,0,5},求最大子向量和的过程如下:
(0)遍历向量的第0个元素-8,可以得知当前子向量{-8}的和为-8,此时子向量{-8}具有最大子向量和,最大子向量和是-8,

    因为-8是负数,所以将子向量和重新设置为0,从向量的下一个元素-7开始继续寻找有可能具有最大和的连续子向量;
(1)遍历向量的第1个元素-7,可以得知当前子向量{-7}的和为-7,大于之前的最大子向量和-8,

    所以此时子向量{-7}具有最大子向量和,最大子向量和是-7,

    因为-7是负数,所以将当前子向量和设置为0,从向量的下一个元素3开始继续寻找有可能具有最大和的连续子向量;
(2)遍历向量的第2个元素3,可以得知当前子向量{3}的和为3,大于之前的最大子向量和-7,

     所以此时子向量{3}具有最大子向量和,最大子向量和是3;
(3)遍历向量的第3个元素2,可以得知当前子向量{3,2}的和为5,大于之前的最大子向量和3,

     所以此时子向量{3,2}具有最大子向量和,最大子向量和是5;
(4)遍历向量的第4个元素0,可以得知当前子向量{3,2,0}的和为5,与之前的最大子向量和相等,

     所以此时子向量{3,2}具有最大子向量和,最大子向量和是5;
(5)遍历向量的第5个元素5,可以得知当前子向量{3,2,0,5}的和为10,大于之前的最大子向量和5,

     所以此时子向量{3,2,0,5}具有最大子向量和,最大子向量和是10;
至此向量中所有的元素都已经遍历完了,可以得知,子向量{3,2,0,5}具有最大子向量和,最大子向量和是10。
需要注意的是:若是存在多个子向量,则输出起始元素下标最小的那个。
因此对于测试数据{0,1,2,0},满足题意的子向量是{0,1,2},而不是{1,2}。
AC代码如下:

#include<stdio.h>
 
/**
*  获取具有最大和的连续子向量的向量和,向量起点和向量终点
*  思路:从头到尾扫描向量,遇到以下两种情况后进行相应的处理:
*  (1)如果当前子向量的和大于之前子向量的最大和,则更新连续子向量的最大和,子向量起点,子向量终点
*  (2)如果当前子向量的和小于0,将该连续子向量看成一个等于其和的负数,
*     然后将下一个向量元素做为新连续子向量的起点,继续寻找有可能具有最大和的连续子向量
*  @param n  输入向量的长度
*  @return void
*/
void getMaxSumInContinueArray(int n)
{
   int start,end;           // 和最大的连续子向量的起点和终点
   int potentialStart;      // 连续子向量的潜在起点
   int maxSum;              // 连续子向量的最大和
   int sum;                 // 实时存放某个连续子向量的和
   int number;              // 向量中的元素
   int i = 0;
   maxSum = -1001;          // 因为输入向量中每个数字的取值范围是[-1000,1000],所以maxSum的初始值为-1001
   sum = 0;
   potentialStart = 0;
   while(i < n)
   {
      scanf("%d",&number);
      sum += number;
      if(sum > maxSum)              // (1)如果和大于当前的最大和,则更新最大和maxSum和start,end
      {
         maxSum = sum;
         start = potentialStart;    // 将潜在的起点赋值为新的起点
         end = i;
      }
      if(sum < 0)  // (2)当某个连续子向量的和小于0时,更新potentialStart和sum
      {
          potentialStart = i + 1;   // 更新潜在起点potentialStart为当前的下一个位置
          sum = 0; // 更新sum = 0,重新开始寻找下一个有可能具有最大和的连续子向量
      }
      i++;
   }
   printf("%d %d %d\n",maxSum,start,end);
}
 
int main()
{
 
    int n;
    while(EOF != scanf("%d",&n) && 0 != n)
    {
        getMaxSumInContinueArray(n);
    }
    return 0;
}
 
/**************************************************************
    Problem: 1372
    User: blueshell
    Language: C
    Result: Accepted
    Time:460 ms
    Memory:912 kb
****************************************************************/


你可能感兴趣的:(面试题,剑指offer)