数列分段---二分+贪心

题目描述

对于给定的一个长度为N的正整数数列A-iA−i,现要将其分成M(M≤N)M(M≤N)段,并要求每段连续,且每段和的最大值最小。

关于最大值最小:

例如一数列4 2 4 5 142451要分成33段

将其如下分段:

[4 2][4 5][1][42][45][1]

第一段和为66,第22段和为99,第33段和为11,和最大值为99。

将其如下分段:

[4][2 4][5 1][4][24][51]

第一段和为44,第22段和为66,第33段和为66,和最大值为66。

并且无论如何分段,最大值不会小于66。

所以可以得到要将数列4 2 4 5 142451要分成33段,每段和的最大值最小为66。

输入输出格式

输入格式:

第11行包含两个正整数N,M。

第22行包含NN个空格隔开的非负整数A_iAi​,含义如题目所述。

输出格式:

一个正整数,即每段和最大值最小为多少。

输入输出样例

输入样例#1: 复制

5 3
4 2 4 5 1

输出样例#1: 复制

6
#include<iostream>
#include<vector>
using namespace std;
int arr[100002];
int n, m;
int lef, rig, mid;
int tu, ts;
bool judge(int mid)//用来判别是否每一段都可以小于mid
{
	for (int i = 1; i <= n; i++)
	{
		if (tu + arr[i]<= mid)//如果还是小于mid,就继续加上去
			tu += arr[i];
		else//否则就开启另一段,ts加一,并且设置tu为当前的数字
		{
			ts += 1;
			tu = arr[i];
		}
	}
	return ts >= m;
}
int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
	{
		cin >> arr[i];
		rig += arr[i];
		lef = lef > arr[i] ? lef : arr[i];
	}
	while (lef<=rig)
	{
		mid = (lef + rig) / 2;
		ts = tu = 0;
		if (judge(mid))//如果最后的ts是大于m的话,说明mid太小了,需要lef往右移
			lef = mid + 1;
		else//否则就是mid太大了
			rig = mid - 1;
	}
	cout << lef << endl;
	system("pause");
	return 0;
}

解析:

最大值的最小值:二分的标志

我们的二分是从这个数列中的最大值到数列总和,为什么?

从最大值开始是因为这个序列最少一段的数量是一个数字,还要这一段数字最大,所以要从最大的数开始。到总和结束是因为小于总和的值都有可能是最后的答案,需要二分来判别

具体见注释

猜你喜欢

转载自blog.csdn.net/scwMason/article/details/87207388