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