题意
给一个长度为n的区间,找两个不相交的区间,区间的长度不确定。
思路
这个题和数学考试那一题唯一不同的是区间的长度不确定了。
在数学考试那个题中,我们是记录了一个maxn[i]
来表示i~n中的区间为k的最大值,现在区间不确定了。
maxn[i]=max(maxn[i+1],sum[j]-sum[i-1]) j>i.
原来j-i的区间是固定的,现在j不确定,但是我们如果想让sum[j]-sum[i-1]的值最大,我们肯定要尽量要让sum[j]最大,所以我们可以在维护一个maxsum[i]表示i~n 中的前缀和数组的最大值,
这样我们的maxn[i]就是维护的i~n中的区间和的最大值了。
但是左边那个区间1~i中因为区间不确定,所以我们还是需要两层循环来解决,但是其实并不需要,根据上面求右区间最大值的方法,我们也可以求出左区间最大值,所以最后的时间复杂度是o(n)的。
#include <bits/stdc++.h>
#define ll long long
#define fi first
#define se second
#define pb push_back
#define me memset
const int N = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
using namespace std;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
ll a[N]; //原数组
ll sum[N]; //前缀和数组
ll maxnr[N]; // maxnr[i] 表示 i~n中的区间最大值。
ll maxsum[N]; // maxsum[i] 表示i~n中的前缀和最大值。
ll maxnl[N]; // maxnl[i] 表示1~i中的区间最大值。
ll minsum[N]; //minsum[i] 表示1~i中的前缀和最小值。
int main()
{
int t;
cin>>t;
while(t--)
{
int n;
cin>>n;
for(int i=1 ; i<=n ; i++) cin>>a[i];
for(int i=1 ; i<=n ; i++) sum[i]=sum[i-1]+a[i];
for(int i=n ; i>=1 ; i--) maxsum[i]=max(maxsum[i+1],sum[i]);
for(int i=1 ; i<=n ; i++) minsum[i]=min(minsum[i-1],sum[i]);
for(int i=n ; i>=1 ; i--)
{
maxnr[i]=max(maxnr[i+1],maxsum[i]-sum[i-1]);
}
for(int i=1 ; i<=n ; i++)
{
maxnl[i]=max(maxnl[i-1],sum[i]-minsum[i]);
}
ll ans=0;
for(int i=1 ; i<n ; i++)
{
ans=max(maxnl[i]+maxnr[i+1],ans);
}
cout<<ans<<endl;
}
return 0;
}
总结
另外,维护 i 右边的所有子区间的和的最大值还有一种办法,这是使用的动态规划的思想——用f[i]表示必须要选i的情况下i向右最大的区间和, f[i]=max(a[i],a[i]+f[i+1]),然后maxn[i]=max(f[i],maxn[i+1])。
二:区间数目变多——找 m个长度为 k 的不相交区间使得他们的权值和最大 ( 1≤n≤5000)
三:区间数目变多且不限制长度——找 m 个不相交长度不限的区间使得他们权值和最大( 1≤n≤5000)
变形二和变形三真的不会,题解都看不懂,等以后再来补吧。
https://ac.nowcoder.com/discuss/392146