【思维题】CodeForce 1000B Light It Up

版权声明:Johnson https://blog.csdn.net/m0_38055352/article/details/91653686

这段时间要沉迷刷题一段时间了,就让CSDN陪我一起吧!

一、题目大意

题目的大致意思是,有一个台灯,给定一个长度为n的数组a,台灯从时刻0开始开灯,每遇到
数组a中的时刻,就切换一次灯的状态,直到到达时刻M,M也是给定的值。现在你可以在给定的数组中的人任意位置插入一个数,这个数也与其他的a[i]起到同样的作用,当然你也可以选择不插入,最终的目的就是使得台灯开灯的时长最长。

二、题目思路以及AC代码

这道题一看给的数据量,M是1e9,肯定不会遍历它,那这么一想,也就只能遍历n了,既然遍历n就要考虑段,而不能只考虑每个数。

对于这个问题,我们可以将其再抽象一下,就是给定了若干个段,然后要求你将其中一个段切分或者不操作,而最终是的所有的奇数段的和最大。

而进一步考虑,我们将其中的一个段切分之后,最终所有的奇数段之和就由三部分组成,一部分是被切段左部的奇数段,也就是原来的数组的左部的奇数段;一部分是被切段右部的奇数段,这部分是原来数组的右部的偶数段,因为切之后多了一段,奇偶性要进行对调;还有一部分就是被切部分的奇数段,为了保证被切之后最大,所以切点肯定是在靠近端点的位置,所以无论该段是奇数段还是偶数段,这部分的时长都是原来的段长度-1.

所以我们只需要对所有的段进行遍历,求得每段上述三部分之和的最大值,也就是最终结果了。当然这里为了减少时间复杂度,要提前维护两个数组,一个LO[i]表示第i段之前所有奇数段的和,一个RE[i]表示第i段之后所有偶数段的和,我在之后代码的注释中也有体现,这样,整个算法的复杂度就可以保持在O(n)了。

下面给出AC代码:

#include <iostream>
#include <algorithm>
#define MAXN 100010
using namespace std;

typedef long long ll;

int n, M;
int a[MAXN];
ll LO[MAXN];		// LO[i] 第i段左边的奇数段的和
ll RE[MAXN];		// RE[i] 第i段右边的偶数段的和

int main()
{
	cin >> n >> M;

	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	a[0] = 0;
	a[n + 1] = M;

	ll ans = 0;
	for (int i = 1; i <= n + 1; i++) {
		if (i & 1) ans += (a[i] - a[i-1]);
	}

	for (int i = 2; i <= n+1; i++) {
		if (i & 1) {
			LO[i] = LO[i - 1];
		}
		else {
			LO[i] = LO[i - 1] + a[i - 1] - a[i - 2];
		}
	}
	for (int i = n; i >= 1; i--) {
		if (i & 1) {
			RE[i] = RE[i + 1] + a[i + 1] - a[i];
		}
		else {
			RE[i] = RE[i + 1];
		}
	}

	for (int i = 1; i <= n + 1; i++) {
		ans = max(ans, LO[i] + RE[i] + a[i] - 1 - a[i - 1]);
	}

	cout << ans << endl;

    return 0;
}

如果有问题,欢迎大家指正!!!

猜你喜欢

转载自blog.csdn.net/m0_38055352/article/details/91653686