《算法进阶指南》—0x04 :Best Cow Fences(思维+二分+前缀和+双指针★★★☆)

图片说明
题意:
农夫约翰的农场由 N 块田地组成,每块地里都有一定数量的牛,其数量不会少于 1 头,也不会超过 2000 头。约翰希望用围栏将一部分连续的田地围起来,并使得围起来的区域内每块地包含的牛的数量的平均值达到最大。围起区域内至少需要包含 F 块地,其中 F 会在输入中给出。在给定条件下,计算围起区域内每块地包含的牛的数量的平均值可能的最大值是多少。

输入格式:
第一行输入整数 N 和 F,数据间用空格隔开。
接下来 N 行,每行输入一个整数,第 i+1 行输入的整数代表第 i 片区域内包含的牛的数目。

输出格式:
输出一个整数,表示平均值的最大值乘以 1000 再向下取整之后得到的结果。

数据范围:
1≤N≤100000
1≤F≤N

思路:
灵活的运用二分:
这里要我们求连续不小于f块地的栅栏所围的奶牛的平均数的最大值。
根据要求可知,所求平均数最大值一定在每单个区域奶牛数量的最大值和最小值之间。
那么现在就可以很明显的看出,这是一个二分查找左区间的右边界点的题目。
我们只需从每单个区域奶牛数量的最大值和最小值之间二分查找答案即可。
—————————————————————————————————————————
这题思路不算难,但是实现过程中有些细节需要注意:
1. check()函数的设计:
我们只需判断是否能取得平均值大于mid,若能则返回1,否则返回0。

这里我采用的是两个for循环+前缀和的方式来遍历,当取得的值大于mid时,则返回1.
若遍历完了所以情况仍未取得,则返回0。
bool check(double mid) {
	for (int i = 1; i <= n-f+1; i++) {
		for (int j = i + f - 1; j <= n; j++) {
			if (s[j]-s[i-1]>=mid*(j-i+1))return 1;//s[]数组为每个位置奶牛数量的前缀和数组。
		}
	}
	return 0;
}
最坏O(n^2)复杂度,交题即可获得20分钟的奖励🤪

然后我仔细思索(csdn)
找到了这个双指针+前缀和方法:

bool check(double mid) {
    for (int i = 1; i <= n; i++) {
        s[i] = s[i - 1] +cow[i]- mid;
    }
    double m = 2000;
    for (int i = 0, j = f; j <= n; j++,i++) {
        m = min(m, s[i]);
        if (s[j] - m >= 0)return 1;
    }
    return 0;
}
此时s[]数组为每个位置的奶牛数量减去mid的前缀和,所以我们只需找到在大于等于f的
长度内(j-i>=f),找到s[j]-s[i]>0,即可说明能取得比mid大的平均值。
细节优化:
    定义一个m,记录每次i以及i位置前的区域的s[i]最小值,我们拿s[j]减去这个最小值
即可得到最优解。

2题意要求:这里题目要求我们将结果乘1000然后向下取整

所以我们要注意二分模板应该精确到至少0.00001并且返回的是右边界。
double bsearch(double l,double r){
    	while (r - l > 1e-5) {
		double mid = (l + r) / 2;
		if (check(mid))l = mid;
		else r = mid;
	}
    return r;
}


完整代码

/*《算法进阶指南》—0x04 二分*/
#include<iostream>
const int N = 100005;
double cow[N], s[N];
double l, r;
int n, f;
using namespace std;
bool check(double mid) {
	for (int i = 1; i <= n; i++) {
		s[i]= s[i - 1] +cow[i]- mid;//每个位置减mid取前缀和
	}
	double m = 2000;
	for (int i = 0, j = f; j <= n; j++,i++) {
		m = min(m, s[i]);
		if (s[j] - m >= 0)return 1;
	}
	return 0;
}
double bsearch(double l,double r){
    	while (r - l > 1e-5) {
		double mid = (l + r) / 2;
		if (check(mid))l = mid;
		else r = mid;
	}
    return r;
}
int main() {
    std::ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
	cin >> n >> f;
	l=0;r=0;
	for (int i = 1; i <= n; i++) {
		cin >> cow[i];
		l = min(l, cow[i]);
		r = max(r, cow[i]);
	}
    r=bsearch(l,r);
	cout << int(1000 * r) << endl;
}
posted @ 2021-07-17 20:33  JDD!  阅读(214)  评论(0)    收藏  举报