题意:给你K本书分给M个人抄,这M个人抄书的速度相同,每个人抄的书的编号必须连续,问怎么划分可以最快的抄完这M本书?
思路:典型的最大值最小化的问题,二分答案,对于当前答案看能否分成M段或者小于M段,求一个满足要求的下界值。主要是判断函数怎么写?首先我们会进行一段一段的划分,因为题目中要求当剩余元素等于字典序最小,所以我们应该让后面的段尽量多,所以我们就从后面往前划分。划分时有三种情况
<1>未划分的段数等于未划分的元素个数,此时应该每个元素一段,证明当前值可行返回true
<2>当前段中的总值小于等于当前二分的答案M,我们就继续往这个段添加元素,如果大于我们就将i+1及其后面的元素划分为一段(因为是从后往前划分的)
<3>需要划分的段数大于M就证明当前值不可行返回false
#include<bits/stdc++.h>
using namespace std;
const int MAXN = 5e2 + 5;
long long k,M,a[MAXN],Max;
bool vis[MAXN];//标记在哪个数字后面划段
bool check(long long m)
{
memset(vis,0,sizeof(vis));
long long sum = a[k - 1],group = 1;//sum用来计算当前段的和,group用来记录当前正在分第几段
for(int i = k - 2; i >= 0; i--)
{
if(a[i] > m) return false;//有元素的值大于m
if(i + 1 > M - group)//剩余的元素个数 > 剩余的组数
{
sum += a[i];
if(sum > m)
{
vis[i] = true;
sum = a[i];
group++;
if(group > M) return false;
}
}
else if(i + 1 == M - group)//剩余的元素个数 == 剩余的组数
{
for(int j = i; j >= 0; j--)
{
if(a[j] > m) return false;//有元素的值大于m
vis[j] = true;
}
return true;
}
}
return true;
}
int main()
{
int T;
scanf("%d",&T);
while(T--)
{
Max = 0;//用来统计一个二分上限值
scanf("%lld%lld",&k,&M);
for(int i = 0; i < k; i++)
{
scanf("%lld",&a[i]);
Max += a[i];
}
long long l = 0,r = Max,m = l + (r - l) / 2;
while(l < r)
{
if(check(m) == false) l = m + 1;
else r = m;
m = l + (r - l) / 2;
}
check(m);
for(int i = 0; i < k; i++)
{
printf("%lld",a[i]);
if(i != k - 1) printf(" ");
if(vis[i]) printf("/ ");
}
printf("\n");
}
return 0;
}
/*
8
9 3
100 200 300 400 500 600 700 800 900
5 4
100 100 100 100 100
4 2
1 2 4 8
6 2
1 2 4 8 4 2
6 2
1 2 3 3 2 1
2 2
10000000 10000000
6 3
1000000 1 1 1 1 1
6 6
6 5 4 3 2 1
*/