您的位置:首页 > 其它

算法学习笔记三:(dp)最长连续子序列

2019-03-04 16:56 127 查看
版权声明:转载请说明出处 https://blog.csdn.net/qq_34448101/article/details/88124728

题目描述
给定 K 个整数的序列{ N1, N2, …, NK } ,其任意连续子序列可表示为{ Ni, Ni+1,…,Nj} ,其中1 <= i<= j <= K。最大连续子序列是所有连续子序列中元素和最大的一个,例如给定序列{ -2, 11, -4, 13, -5, -2 } ,其最大连续子序列为{ 11, -4, 13 } ,最大和为20。编写程序得到其中最大子序列的和并输出该子序列的第一个和最后一个元素的下标。
输入
测试输入包含若干测试用例,每个测试用例占2 行,第 1 行给出正整数 K( <100000) ,第 2 行给出 K 个整数,每个整数的范围-10000至10000 ,中间用空格分隔。当K 为0 时,输入结束,该用例不被处理。
输出
对每个测试用例, 在 1 行里输出最大和、 最大连续子序列的第一个和最后一个元素的下标,中间用空格分隔。 如果最大连续子序列不唯一, 则输出序号 i 和 j 最小的那个(如输入样例的第 2、3组)。若所有 K 个元素都是负数,则定义其最大和为0,输出"0 0 0"。
样例输入
8
6 -2 11 -4 13 -5 -2 10
20
-10 1 2 3 4 -5 -23 3 7 -21 6 5 -8 3 2 5 0 1 10 3
8
-1 -5 -2 3 -1 0 -2 0
4
-1 -2 -4 -3
0
样例输出
27 0 7
27 10 19
3 3 3
0 0 0

思想:首先此问题若用暴力解法,枚举左端点和右端点需要O(n^2)的时间复杂度这样对n为10 ^5的数据来说无法承受。因此可以用动态规划来解决。
第一步:设置数组dp表示为以a[i]作为末尾的连续序列的最大和。样例:序列-2 11 -4 13 -5 -2,下标为0,1,2,3,4,5那么有

dp[0]=-2;  dp[1]=11;  dp[2]=7(11+(-4)=7);  dp[3]=20(11+(-4)+13=20);  dp[4]=15;  dp[5]=13.

第二步:由于dp[i]要求是必须以a[i]为结尾的连续队列,所以只有两种情况:
①这个最大和的连续序列只有一个元素,即从a[i]开始到a[i]结束。
②这个最大和的连续序列有多个元素,即从前面某处a[p]开始(p<i)一直到a[i]结束。
对于第一种情况,最大和就是a[i]本身。
对于第二种情况,最大和就是dp[i-1]+a[i].

可得到状态转移方程:

dp[i]=max{a[i],dp[i-1]+a[i]}.

最后遍历dp数组,其中最大值就是所求。奉上代码.

#include<iostream>
using namespace std;
struct node
{
int x,y;//x为起点,y为终点
long long a;
};
int main()
{
int k;
while(scanf("%d",&k)!=EOF)
{
long long a[10010];
if(k==0)
break;
int i,max=0;
for(i=0;i<k;i++)
cin>>a[i];
node dp[10010];
dp[0].a=a[0];
dp[0].x=0;
dp[0].y=0;
for(i=1;i<k;i++)
{
if(a[i]>dp[i-1].a+a[i])
{
dp[i].a=a[i];
dp[i].x=i;
dp[i].y=i;
}
else
{
dp[i].a=dp[i-1].a+a[i];
dp[i].x=dp[i-1].x;
dp[i].y=i;
}
}
for(i=1;i<k;i++)
{
if(dp[i].a>dp[max].a)
max=i;
}
if(dp[max].a<0)
cout<<"0 0 0"<<endl;
else
cout<<dp[max].a<<" "<<dp[max].x<<" "<<dp[max].y<<endl;
}
return 0;

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: