题目大意:
按顺序给你N个数,将这N个数分成连续的M段,使得这M段每段的和中的最大值最小,输出最小值(1<=N<=100000,1<=M<=N,每个数在1到10000之间),如果有多种可能的话,尽量在前面进行划分。
思路:
最大值最小化的问题,利用二分法,下界是0 上界是所有数字的和。然后去查找小于等于mid可以划分为几组,如果可以划分的组数大于M的话,那么就表示说这个值太小了,应该l = mid +1;否则, r =mid;
代码:
#include <iostream>
using namespace std;
#include <stdio.h>
#include <cstring>
// 题目要求尽量往前面划分 所以要从尾巴开始往前面利用贪心思想尽可能多的划分到一个区间中去
const int MAXN = 510;
int a[MAXN],used[MAXN];
long long low,high;
int n,m;
bool judge(int k) {
long long sum = 0;
int group = 1;
for(int i = n-1; i >= 0; i--) {
if(sum + a[i] > k) {
group ++;
sum = a[i];
if(group > m) return false; //表示所取的这个值太小了
}
else
sum += a[i];
}
return true; //取的这个值>=
}
void print(int k) {
long long sum = 0;
int group = 1;
for(int i = n - 1; i >= 0; i--) {
if(sum + a[i] > k) {
used[i] = 1; //如果i加上sum已经大于K了,就必须在i的后面画一个/
sum = a[i];
group++;
}
else
sum += a[i];
if(m - group == i + 1) {//此时剩下要划分的组数刚好等于剩下的
for(int j = 0;j <= i; j++)
used[j] = 1;
break;
}
}
for(int i = 0 ; i < n-1; i++) {
printf("%d ",a[i]);
if(used[i]) printf("/ ");
}
printf("%d\n",a[n - 1]);
}
int main() {
int k;
scanf("%d",&k);
while(k--) {
memset(a,0,sizeof(a));
memset(used,0,sizeof(used));
low = -1,high = 0;
scanf("%d %d",&n,&m);
for(int i = 0 ; i < n; i++) {
scanf("%d",&a[i]);
if(low < a[i]) low = a[i];
high = high + a[i];
}
while(low < high) {
long long mid = (low + high)/2;
if(judge(mid)) {
high = mid;
}
else
low = mid + 1;
}
print(low);
}
return 0;
}