题目要求
题目大意为:输入一个n列数组,分成m段,对数组输出一个正整数,即每段和最大值最小为多少。
例如:
5 3
4 2 4 5 1
最小为【4】、【2 4】、【5 1】,分组中,最小的最大组值为6。
解题思路
本题用到二分查找的算法。
二分的具体用法如下:
先确定一个数组:有的是题目直接给的,有的需要自己推测题意寻找数组。前者比较简单,直接排好序,确定好left=0;right=n-1。**再让middle=(left+right)/2,与要查找的数据find作比较。如果middle<=find,让left=middle+1,否则right=middle-1。**前面一句进行循环,直至left>=right为止,说明答案找到。
二分算法的时间复杂度为:O()=O(logn)。
但是这一题的难度在于,怎么找到要进行二分查找的数组?我们可以往极限的方向去想,如例子:
5 3
4 2 4 5 1 //如果分组3改为1,那么,答案一定是数组中最大的“5”,如果分组改为5,那么答案为数组数据之和。
所以,要进行查找的数组为:left=max(a[n]),right=sum(a[n]),middle=(left+right)/2,问题又出来了,中间值出来了,与谁作比较呢?因为要把数组分组,所以可以遍历a数组,将遍历的sum(a[i])与middle作比较,如果大了,继续加;如果小了就记录一下(num++),最后,看看小了的那些组之和(num)是不是比m小,如果小的话,说明答案藏在左半部分,否则,答案藏在右半部分。
在用二分查找的时候,切记切记,一定要把边界找好喽!
正确代码
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
int main()
{
int rin=0,len=0; //二分的左右边界,一定要清零!!!
int n,m;
cin>>n>>m;
int a[n];
for(int i=0;i<n;i++){ //O(n)时空复杂度
cin>>a[i];
rin+=a[i]; //这里二分的右边界为数组所有数字之和
len=len>a[i]?len:a[i];//这里二分的左边界为数组中最大的数字。
}
while(len<=rin){ //当还没找到时,O(nlogn)时间复杂度
int sum=0,num=0;
int mi=(len+rin)/2; //二分
for(int i=0;i<n;i++){ //遍历当前的mi值是大了还是小了。
if(sum+a[i]<=mi)
sum+=a[i];
else{
sum=a[i];
++num;
}
}
if(num>=m) len=mi+1; //答案在后半部分
else rin=mi-1; //答案在左半部分
}
cout<<len;
return 0;
}
本题时间复杂度:O(nlogn)