P1121 环状最大两段子段和

链接

https://www.luogu.com.cn/problem/P1121

题目

思路

这个O(n)的思路很好:https://www.cnblogs.com/kamimxr/p/11438701.html。 关键思路:

答案分两种情况,一种是选择的两段均不跨越n到1(也就是环),另一种是选择的两段跨过了环;

如果均不跨越环,那么也就是意味着这是一道模板题;

设maxl[i]表示从1-i的最大字串和,maxr[i]表示i-n的最大字段和;

很明显的:答案就是max(maxl[i]+maxr[i+1])

maxl[i]=max(a[i],maxl[i-1]+a[i]);//表示这一位是不是一段区间的开头;
maxl[i]=max(maxl[i-1],maxl[i]);
maxr[i]=max(a[i],maxr[i+1]+a[i]);
maxr[i]=max(maxr[i],maxr[i+1]);

然后处理环:
我们换种思维,就好比并查集维护联通时可以运用逆向思维一样,把拆边改为加边,你求1~n带环的最大值就等价于求(1-n的区间和)减去(1-n的两段区间最小值)
补充一个解释:

红色是跨端点连接成一段,那么最终形成的另外两段就只需要求最小值就行了。

代码

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<algorithm>
#include<queue>
#include<cmath>
#include<map>
#include<string.h>
#include<limits.h>
#include<string>
#include<vector>
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;
#define int long long 
const int N = 2e5 + 10;
int a[N];
int sumn;
int maxl[N], maxr[N], minl[N], minr[N];


signed main()
{
	IOS;
	int n; cin >> n;
	int cnt = 0;
	for (int i = 0; i < n; i++)
	{
		cin >> a[i]; 
		sumn += a[i];
		if (a[i] > 0)cnt++;
	}
	int ans = 0;
	if (cnt <= 2)
	{
		priority_queue<int>pq;
		for (int i = 0; i < n; i++)pq.push(a[i]);
		ans += pq.top();
		pq.pop();
		ans += pq.top();
		pq.pop();
	}
	else
	{
        //注意这里一定要分开求max!!分两步,第一步决定是否是这个数开头,第二步才是对历史信息的综合。如果过早求max会丢失信息
		for (int i = 0; i < n; i++)
		{
			if (i == 0)maxl[i] = a[i], minl[i] = a[i];
			else maxl[i] = max(a[i],  maxl[i - 1] + a[i]), minl[i] = min(a[i],  minl[i - 1] + a[i]);
		}
		for (int i = 1; i < n; i++)
		{
			maxl[i] = max(maxl[i], maxl[i - 1]), minl[i] =  min(minl[i - 1], minl[i] );
		}
		for (int i = n - 1; i >= 0; i--)
		{
			if (i == n - 1)maxr[i] =a[i], minr[i] =a[i];
			else maxr[i] = max(a[i],maxr[i + 1] + a[i]), minr[i] = min(a[i], minr[i + 1] + a[i]);
		}
		for (int i = n - 2; i >= 0; i--)
		{
			maxr[i] =max(maxr[i + 1], maxr[i]), minr[i] =min(minr[i + 1], minr[i]);
		}
		int ansmax = 0, ansmin = LLONG_MAX;
		for (int i = 0; i + 1 < n; i++)
			ansmax = max(ansmax,maxl[i] + maxr[i + 1]),
			ansmin = min(ansmin,minl[i] + minr[i + 1]);
		ans = ansmax;
		if (ansmin != LLONG_MAX and sumn - ansmin > ans)ans = sumn - ansmin;
	}
	cout << ans;
	return 0;
}

posted @ 2025-02-15 16:10  WHUStar  阅读(61)  评论(0)    收藏  举报