链接:http://acm.hdu.edu.cn/showproblem.php?pid=1231
题意:多组样例,以0结束。给你一个n,接下来n个数,求出这n个数的最大连续子序列的和以及这个连续子序列的下标(l,r),要求这个l,r尽可能的小。
思路1(单调队列做法):先求出前缀和,然后用双向队列维护一个严格单调递增的队列。这样就可以对于每一个i,都求一个在它前面最小的前缀和,减去就是以i为终点的最大连续子序列。注意只取当前i位置上,这一个数的情况。
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e4+10;
int n,l,r;
ll a[N],pre[N],ans;
bool flag;
int main(void)
{
while(~scanf("%d",&n)&&n)
{
deque<int> q;
ans=-1e18;
flag=1;
l=n+1;
r=0;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
if(a[i]>=0) flag=0;
pre[i]=pre[i-1]+a[i];
}
if(flag)
{
printf("0 %lld %lld\n",a[1],a[n]);
continue;
}
for(int i=1;i<=n;i++)
{
while(!q.empty()&&pre[q.back()]>=pre[i])
q.pop_back();
if(!q.empty())
{
if(ans<pre[i]-pre[q.front()])
{
l=q.front()+1;
r=i;
ans=pre[i]-pre[q.front()];
}
}
else if(a[i]>ans)
{
ans=a[i];
l=i;
r=i;
}
q.push_back(i);
}
printf("%lld %lld %lld\n",ans,a[l],a[r]);
}
return 0;
}
思路2(简单dp):dp[i]:表示以i为结尾的最大连续子序列的和。
转移方程:dp[i]=dp[i-1]<0?a[i]:dp[i-1]+a[i];
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e4+10;
int n,l,r,minl;
ll a[N],dp[N],ans;
bool flag;
int main(void)
{
while(~scanf("%d",&n)&&n)
{
ans=-1e18;
flag=1;
l=r=minl=1;
for(int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
if(a[i]>=0) flag=0;
if(dp[i-1]<0)
{
dp[i]=a[i];
minl=i;
}
else dp[i]=dp[i-1]+a[i];
if(dp[i]>ans)
{
ans=dp[i];
l=minl;
r=i;
}
}
if(flag)
{
printf("0 %lld %lld\n",a[1],a[n]);
continue;
}
printf("%lld %lld %lld\n",ans,a[l],a[r]);
}
return 0;
}