ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

双子序列最大和问题

2022-04-14 02:04:47  阅读:183  来源: 互联网

标签:最大 int max 最大值 maxn 双子 序列 断点


感谢所有AC

链状

传送门

思路

       由于两个子序列不重叠,显然的这两个子序列之间一定有一个断点。要求两个子序列之和最大值,可以枚举断点的位置,对比每个断点下左序列和右序列的最大值之和,最大的即为答案。

       接下来该怎么求解每一个左序列的最大值和右序列的最大值呢?在这样的数据规模下必然是已经打出了两个序列在每个断点处的最大值的一张表,每次掉用只花常数时间。求解左序列在每个断点的最大值这个问题,很容易想到用dp思想求解单序列最大值,那么常规操作:定义状态 f( i ) 为以序列第 i 个数为结尾的序列最大值。

       不过上述步骤还没得出左序列在每个断点处最大值的表,它得到的是以断点左端点为结尾的左序列的最大值,而在实际情况中,左序列最大值不一定以断点左端为结尾,所以需要更新这个数组 f ,用 f[i] = max(f[i], f[i - 1]) 这样一个语句就可以实现更新(其实这个操作就像求解单序列最大值最后需要遍历一遍数组找出最大值一样,因为真正的最大序列不一定以最后一个数为结尾)。

       对于右序列,只需倒着操作就可实现。

代码

#include<iostream>
#define maxn 1000007
using namespace std;
long long a[maxn], f[maxn], g[maxn], ans = INT64_MIN;
int main(void)
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n = 0;
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		f[i] = g[i] = a[i];
	}
	for (int i = 2; i <= n; i++)
		f[i] = max(f[i], f[i - 1] + a[i]);
	for (int i = 2; i <= n; i++)
		f[i] = max(f[i], f[i - 1]);
	for (int i = n - 1; i >= 1; i--)
		g[i] = max(g[i], g[i + 1] + a[i]);
	for (int i = n - 1; i >= 1; i--)
		g[i] = max(g[i], g[i + 1]);
	for (int i = 1; i <= n - 2; i++)
		ans = max(ans, f[i] + g[i + 2]);
	cout << ans << '\n';
	return 0;
}

环状

传送门

思路

       在双子序列前,应该先明白环状序列的单子序列的最大和求法。环状的情况下这显然变成一个环形dp的问题,可以常规的环形dp一样扩增序列为两倍长,然后枚举断点求解,但是在数据规模到达 1e4 后会TLE,需要寻找更好的解法。

       分析这个单子序列,只有两种可能:0000011111100000 或者 11110000111111 。对于情况一,退化为链状单子序列最大和,容易求解。对于情况二,由于整个序列的和是固定的,那么那段不选的区间显然就是链状单子序列最小和,可以间接地得到情况二的值为序列总和减去单子序列最小和。最后在这两种情况中选出最大的,代码上就只要维护一个链状单子序列最大和链状单子序列最小。

       对于环状双子序列,也有这样两种情况 00111100001111000 或者 11100011110000111 ,情况一退化为链状双子序列最大和,容易求解。情况二,再维护一个链状双子序列最小和即可求解。

       单纯这样写会被 hack!考虑到题目要求一定是两段非空的子序列,那么假如整个序列都是负数,最后答案将是 0 ,这显然不合要求。假如整个序列只有一个正数,最后答案将会是这个正数,也不符合题意。这是由于维护的双子序列最小会不断地选择负数,而当正数个数到达2之后就不会再出现这种情况,因为它保证了子序列最短长度为 1 地要求,所以对于正数个数为 0 或 1 的数据需要特判。

代码

#include<iostream>
#include<cstring>
#include<algorithm>
#define maxn 200007
using namespace std;
int a[maxn], f[maxn], g[maxn], ans_max = INT32_MIN, ans_min = INT32_MAX, sum;
int main(void)
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int n = 0, num = 0;
	cin >> n;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
		sum += a[i];
		if (a[i] >= 0)
			num++;
	}
	if (num == 0||num==1)
	{
		sort(a + 1, a + n + 1);
		cout << a[n] + a[n - 1];
		return 0;
	}
	f[1] = a[1];
	for (int i = 2; i <= n; i++)
		f[i] = max(a[i], f[i - 1] + a[i]);
	for (int i = 2; i <= n; i++)
		f[i] = max(f[i], f[i - 1]);
	g[n] = a[n];
	for (int i = n - 1; i >= 1; i--)
		g[i] = max(a[i], g[i + 1] + a[i]);
	for (int i = n - 1; i >= 1; i--)
		g[i] = max(g[i], g[i + 1]);
	for (int i = 1; i < n; i++)
		ans_max = max(ans_max, f[i] + g[i + 1]);
	memset(f, 0, sizeof(f));
	f[1] = a[1];
	for (int i = 2; i <= n; i++)
		f[i] = min(a[i], f[i - 1] + a[i]);
	for (int i = 2; i <= n; i++)
		f[i] = min(f[i], f[i - 1]);
	memset(g, 0, sizeof(g));
	g[n] = a[n];
	for (int i = n - 1; i >= 1; i--)
		g[i] = min(a[i], g[i + 1] + a[i]);
	for (int i = n - 1; i >= 1; i--)
		g[i] = min(g[i], g[i + 1]);
	for (int i = 1; i < n; i++)
		ans_min = min(ans_min, f[i] + g[i + 1]);
	cout << max(ans_max, sum - ans_min);
	return 0;
}

 

标签:最大,int,max,最大值,maxn,双子,序列,断点
来源: https://www.cnblogs.com/xqk0225/p/16142853.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有