森林的最大美丽值(二分+差分数组)
题目分析
求最小值的最大值,联想到二分。
第一阶段二段性分析
对于所有树的高度都可以大于等于mid,那么我们可以确定高度小于mid的值一定也可以,但是此时我需要找的是最大的高度,那么mid一定比小于mid的值更大,所以小于mid的值我就不用管了,也就是我可以确定我能够舍弃掉mid左边的值。我还想要确定比mid更大的边长是否也满足条件,所以我要在mid的右边继续二分。
if (check(mid)) {l = mid;} //因为mid是符合条件的,所以我要留着它,而不是l=mid+1
对于所有树的高度不能满足都大于等于mid,那么我们可以确定高度大于mid的值一定也不满足,所以大于等于mid的值我就不用管了,也就是我可以确定我能够舍弃掉mid右边的值。我还想要寻找比mid更小的高度是否能满足条件,所以我要在mid的左边继续二分。
else { r = mid - 1; }//因为mid是不符合条件的,所以我不要留着它,而不是r=mid
//主要这里出现了减法,那么求mid那么应该是(l+r+1)/2
综上该题满足二段性,可以用二分,二分的板子就不说了,接下来说一下check函数如何写。
第二阶段写check函数
check(u)要实现的作用是检查能否在m天内让所有树的高度都大于等于u。
依次遍历每一棵树,如果当前树的高度a[i]小于mid,那么需要给他增高,增高耗费的天数是(mid-a[i])。但是我们要注意一旦选择增高不是只给一颗树增高,而是给一个区间里的树进行增高,那么这就涉及到区间修改,我们可以使用差分数组。现在重新考虑遍历每一棵树,当前树的实际高度是a[i]+sum[i],这里的sum[i]是差分数组的前缀和,如果a[i]+sum[i]<x,那么我们要给这棵树增高,耗费的天数和增加的高度是x-a[i]-sum[i],对于差分数组的变化是d[i]+=x-a[i]-sum[i],d[i+k]-=x-a[i]-sum[i],注意这里的i+k可能会有下标越界问题,所以要改成d[min(i+k,n+1)]-=x-a[i]-sum[i],如果耗费的总天数超过了m,即cnt<0
,就返回false,否则返回true。注意这里更改了d[i]后,对应的sum[i]也要随之更新,否则这里d[i]的改变没有传递下去。
public static boolean check(int x) {int d[] = new int[100010];int sum[] = new int[100010];int cnt = m;for(int i=1;i<=n;i++) {sum[i] =sum[i-1]+d[i];if(a[i]+sum[i]<x) {cnt-=x-a[i]-sum[i];d[i]+=x-a[i]-sum[i];d[Math.min(i+k,n+1)]+=sum[i]+a[i]-x;sum[i]+=x-a[i]-sum[i];if(cnt<0) return false;}}return true;
}
第三步二分范围确定
那么这里的高度的最小值是1,最大值就是树的最大高度,也就是1e9+1。
题目代码
import java.util.Scanner;
public class Main {static int n,m,k;static int a[] = new int[100010];public static boolean check(int x) {int d[] = new int[100010];int sum[] = new int[100010];int cnt = m;for(int i=1;i<=n;i++) {sum[i] =sum[i-1]+d[i];if(a[i]+sum[i]<x) {cnt-=x-a[i]-sum[i];d[i]+=x-a[i]-sum[i];d[Math.min(i+k,n+1)]-=x-a[i]-sum[i];sum[i]+=x-a[i]-sum[i];if(cnt<0) return false;}}return true;}public static void main(String[] args) {Scanner scan = new Scanner(System.in);n=scan.nextInt();m=scan.nextInt();k=scan.nextInt();for(int i=1;i<=n;i++) a[i]=scan.nextInt();int l=1,r=1000000001;while(l<r) {int mid=(l+r+1)/2;if(check(mid)) l=mid;else r=mid-1;}System.out.print(l);}
}
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 100010;
int n, m, k;
int a[N];
int d[N], sum[N];bool check(int x) {for (int i = 1; i <= n; ++i) d[i]=0,sum[i]=0;int cnt = m;for (int i = 1; i <= n; ++i) {sum[i] = sum[i - 1] + d[i];if (a[i] + sum[i] < x) {cnt -= x - a[i] - sum[i];d[i] += x - a[i] - sum[i];if (i + k <= n) d[i + k] -= x - a[i] - sum[i];sum[i] += x - a[i] - sum[i];if (cnt < 0) return false;}}return true;
}int main() {cin >> n >> m >> k;for (int i = 1; i <= n; ++i) cin >> a[i];int l = 1, r = 1000000001;while (l < r) {int mid = (l + r + 1) / 2;if (check(mid)) l = mid;else r = mid - 1;}cout << l << endl;return 0;
}
def check(x):d = [0] * (n + 1)sum = [0] * (n + 1)cnt = mfor i in range(1, n + 1):sum[i] = sum[i - 1] + d[i]if a[i] + sum[i] < x:cnt -= x - a[i] - sum[i]d[i] += x - a[i] - sum[i]if i + k <= n:d[i + k] -= x - a[i] - sum[i]sum[i] += x - a[i] - sum[i]if cnt < 0:return Falsereturn Truen, m, k = map(int, input().split())
a = [0] + list(map(int, input().split())) # Using 1-based indexingl, r = 1, 1000000001#+1e5
while l < r:mid = (l + r + 1) // 2if check(mid):l = midelse:r = mid - 1print(l)