做法:贪心+二分
题意解读:
这题是我发的上一篇题解的题目的升级版。大意为给定一个长度为 N N N序列 a i ( i ∈ [ 1 , N ] , i ∈ N ∗ ) a_i(i\in[1,N],i\in\mathbb N^*) ai(i∈[1,N],i∈N∗),将其分成 M M M段,要求每段连续,并且使每段和的最大值最小。
相信大家如果眼睛不瞎 ( L J Z : 我 瞎 呢 ) (LJZ:我瞎呢) (LJZ:我瞎呢)都能看到上面加粗的五个字:最大值最小。当题目中出现了类似的字眼时,就表示大概率这题的答案具有单调性,可以二分解决。 ( O W : 今 年 t g 必 考 二 分 ) (OW:今年tg必考二分) (OW:今年tg必考二分)
思路:
我们设这个最小值为 K K K。我们略加思考可以发现:当 K K K十分的小时,一定可以存在一种分组,使组数 M K > M ( 举 个 例 子 : 每 个 数 自 成 一 组 。 ) M_K>M(举个例子:每个数自成一组。) MK>M(举个例子:每个数自成一组。),此时的 K K K值都是符合题意的。而当 K K K逐渐增长到一个临界点以后, M K M_K MK将小于 M M M,此时的 K K K都不符合题意。所以这个临界点 ( 设 为 K 0 ) (设为K_0) (设为K0)满足:
{ ∀ M x ∈ [ 1 , M 0 ] , M 均 符 合 题 意 。 ∀ M x ∈ ( M 0 , + ∞ ] , M 均 不 符 合 题 意 。 \begin{cases} \forall M_x\in[1,M_0],M均符合题意。\\ \forall M_x\in(M_0,+\infty],M均不符合题意。 \end{cases} {
∀Mx∈[1,M0],M均符合题意。∀Mx∈(M0,+∞],M均不符合题意。
受到这个 M 0 M_0 M0性质的启发,我们定义函数:
C ( x ) = { 0 , x > M 0 1 , x ≤ M 0 C(x)=\begin{cases} 0,x>M_0\\ 1,x\leq M_0 \end{cases} C(x)={
0,x>M01,x≤M0
然后取
l = x m i n = max ( a i ) , 即 M = n 。 r = x m a x = s u m ( a i ) , 即 M = 1 。 l=x_{min}=\max(a_i),即M=n。\\r=x_{max}=sum(a_i),即M=1。 l=xmin=max(ai),即M=n。r=xmax=sum(ai),即M=1。
二分求出临界值即可。
A C c o d e AC code ACcode:
#include<bits/stdc++.h>
using namespace std;
int n, m, a[100005],l,r;
long long s[100005];
inline void read()
{
cin >> n >> m;
for (int i = 1; i <= n;i++)
{
cin >> a[i];
l = max(a[i], l);
r += a[i];
}
}
inline bool C(int x)
{
int tot=0,num=0;
for(int i=1;i<=n;i++)
{
if(tot+a[i]<=x)tot+=a[i];
else tot=a[i],num++;
}
return num>=m;
}
int main()
{
read();
while(l<=r)
{
int mid=(l+r)>>1;
if(C(mid))
{
l = mid+1;
}
else
{
r = mid-1;
}
}
cout << l << endl;
return 0;
}