洛谷P1182 数列分段 Section II

题目描述
对于给定的一个长度为N的正整数数列 A_{1\sim N}A
1∼N

,现要将其分成 MM(M\leq NM≤N)段,并要求每段连续,且每段和的最大值最小。
关于最大值最小:
例如一数列 4 2 4 5 1 要分成 3 段。
将其如下分段:
[4 2][4 5][1]
第一段和为 6,第 2 段和为 9,第 3 段和为 11,和最大值为 9。
将其如下分段:
[4][2 4][5 1]
第一段和为 4,第 2 段和为 6,第 3 段和为 6,和最大值为 6。
并且无论如何分段,最大值不会小于 6。
所以可以得到要将数列 14 2 4 5 1 要分成 3 段,每段和的最大值最小为 6。
输入格式
第 1 行包含两个正整数 N,MN,M。
第 2 行包含 NN 个空格隔开的非负整数 A_iA
i ,含义如题目所述。
输出格式
一个正整数,即每段和最大值最小为多少。
输入输出样例
输入 #1复制
5 3
4 2 4 5 1
输出 #1复制
6
说明/提示
对于 20%20% 的数据,N\leq 10N≤10。
对于 40%40% 的数据,N\leq 1000N≤1000。
对于 100%100% 的数据,1\leq N\leq 10^5 1≤N≤10
5
,M\leq NM≤N,A_i < 10^8
i<10 8 , 答案不超过 10^910
9

题目链接,点这里

这道题要使用贪心和二分的思想来做,我一开始朦朦的,看了洛谷里的题解也没有讲到重点。先上代码吧。

#include
using namespace std;

int n,m,Left=0,Right=0,mid;	
int a[100010];

bool check(int k)  			//寻找当和不超过mid时能分的段数 
{
	int cnt=1,per_sum=0;   //检查分的段数 
	for(int i=0;i<n;i++)	//per_sum 记录目前每一段(未成段)的和 
	{
		if(per_sum+a[i]<=k)
		{
			per_sum += a[i];
		}
		else
		{
			per_sum = a[i];
			 cnt++;
		} 
	}
	return cnt>m;  //这是重点,如果分的段数大于要分的段数 (cnt>m) 
}					//说明mid(k)这个数选小了,所以要找的这个数在mid的右边		
					//如果分的段数小于要分的段数 (cnt<=m)
int main()			//说明mid(k) 这个数选大了,要找的那个数在mid的左边 
{					//如果相等,继续往mid左找 , 贪心嘛再看看有没有更小的 
	cin>>n>>m;
	for(int i=0;i<n;i++)
	{
		cin>>a[i];
		Left = a[i]>Left?a[i]:Left;  //Left 数列中最大的数 
		Right += a[i];				//Rigth 数列的和 
	}
	while(Left<=Right)	//要找的数 每段和的最大值里的最小值 一定在这个区间内 
	{
		mid = (Left+Right)/2;
		if(check(mid))
			Left = mid +1;	//mid(k)这个数选小了,所以要找的这个数在mid的右边	
		else
			Right = mid - 1;  //mid(k) 这个数选大了,要找的那个数在mid的左边 
	}
	cout<<Left;  
} 

**

1 . 要求每段和最大值的最小,那么这个数一定在这个数列的最大值和数列和之间。所以将Left设为数列的最大值,Right设为数列和
2.将问题拆成若干个 数列分段Section I 问题,求当前选的这个最大值的最小(mid),让每段和不大于mid,看看能分多少段。
3.如果分的段数大于要分的段数 (cnt>m) ,说明mid(k)这个数选小了,所以要找的这个数在mid的右边。
4.如果分的段数小于要分的段数 (cnt<=m) , 说明mid(k) 这个数选大了,要找的那个数在mid的左边 。
5. 如果相等,继续往mid左找 , 贪心嘛再看看有没有更小的。
6. 知道二分查找结束,输出结果

**

你可能感兴趣的:(洛谷P1182 数列分段 Section II)