题目:
对于给定的一个长度为N的正整数数列,现要将其分成 M段,并要求每段连续,且每段和的最大值最小。
关于最大值最小:
思路:
这个数列分段后最大的值是在 这列数列中最大的数max到这些数列数加一起的总和sum之间 ,所以这个区间就是我们二分查找的区间。二分查找的这个值就是我们最后的最大值,我们以二分查找的这个值去给数列进行分段,用贪心算法,如果最后分段数太少,说明二分查找的这个最大值太大,right移动,而如果分段数太多,说明二分查找的这个最大值太小,left移动。
这个贪心算法的模型就是:
对于给定的一个长度为 n 的正整数数列 ai ,现要将其分成连续的若干段,并且每段和不超过 m(可以等于 m),问最少能将其分成多少段使得满足要求。
思路:
对于已给出数列,从前往后扫描一遍,在扫描过程中,不断记录当前最大值,与给出m进行比较,若当前和大于m则记录段数,从已扫描的数的最后一个作为下一次扫描的开始。
代码:
#include<iostream> using namespace std; int main() { int n,m; int Thissum=0,count = 1; int a[100000]; cin >> n; cin >> m; for (int i = 0; i < n; i++) cin >> a[i]; for (int j = 0; j < n; j++) { Thissum += a[j]; //记录当前和 if (Thissum >m) { count += 1; Thissum =a[j];/*当前和大于m时, 令当前和等于已处理的最后一个数 从这个数开始往后处理*/ } } cout << count << endl; return 0; }
代码:
#include<stdio.h>
int max(int a,int b){
if(a>b){
return a;
}
else{
return b;
}
}
int main(){
typedef long long ll;
ll n,m,a[100005],sum=0;
scanf("%lld %lld",&n,&m);
int i;
ll max1=0;
for(i=0;i<n;i++){
scanf("%d",&a[i]);
sum+=a[i];
max1=max(max1,a[i]);
}
ll left=max1;
ll right=sum;
while(left<=right){
ll mid=(left+right)/2;
int t=0;
int cnt=1;
for(i=0;i<n;i++){
t+=a[i];
if(t>mid){
t=a[i];
cnt++;
}
}
if(cnt<=m){
right=mid-1;
}
else{
left=mid+1;
}
}
printf("%lld",right+1);
}