题目链接地址:
九度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 ****************************************************************/