题目
Best Cow Fences
引言
对于二分,我们先引入模板。二分模板一共有两个,分别适用于不同情况,mid在哪一边,版本1属于左半边,版本2属于右半边。
算法思路:假设目标值在闭区间[l, r]中, 每次将区间长度缩小一半,当l = r时,我们就找到了目标值。
版本1
当我们将区间[l, r]划分成[l, mid]和[mid + 1, r]时,其更新操作是r = mid或者l = mid + 1;,计算mid时不需要加1。
int bsearch_1(int l, int r)
{while (l < r){int mid = l + r >> 1;if (check(mid)) r = mid;else l = mid + 1;}return l;
}
版本2
当我们将区间[l, r]划分成[l, mid - 1]和[mid, r]时,其更新操作是r = mid - 1或者l = mid;,此时为了防止死循环,计算mid时需要加1。
int bsearch_2(int l, int r)
{while (l < r){int mid = l + r + 1 >> 1;if (check(mid)) l = mid;else r = mid - 1;}return l;
}
对于实数二分,精度由eps决定,若需要保持k位,eps位1e(-k-2),比如保留四位eps=1e-6。
int bsearch_3(int l, int r)
{double eps = 1e-6;while (r - l > eps){double mid = l + r >> 1;if (check(mid)) l = mid;else r = mid;}return l;
}
题解
回归正题,二分的使用不一定在于单调性,而在于他是否能将一个区间分成两半,一半满足,一半不满足。那么我们判断是否存在一个平均值大于等于mid,如果最优解是x,那么mid <= x的时候,必然可以找到一段,其平均值≥mid,否则一定找不到。我们可以将每个数减去平均数mid,若大于0说明它本身大于平均数,若小于0说明他本身小于平均数,然后对于一个区间和来说大于0,就是说明它的平均数比mid大。而区间和可以用前缀和的思想来实现,即sum[i]-sum[j]>0就是存在一个区间[j+1,i]满足平均数大于mid,而这里的j是可以从1枚举到i-m+1的,取他们中间最小的就行了。
参考代码
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e5 + 10;
int n, m;
int cow[N];
double sum[N];
bool check(double avg){for(int i = 1; i <= n; i ++) sum[i] = sum[i - 1] + cow[i] - avg;double minv = 0;for(int i = m, j = 0; i <= n; i ++, j ++){minv = min(minv, sum[j]);if(sum[i] >= minv) return true;}return false;
}
int main(){cin >> n >> m;for(int i = 1; i <= n; i ++) cin >> cow[i];double l = 0, r = 2000;while(r - l > 1e-5){double mid = (l + r) / 2;if(check(mid)) l = mid;else r = mid;}cout << (int)(r * 1000) << endl;return 0;
}