AcWing 102. 最佳牛围栏(前缀和+二分+DP)

AcWing 102. 最佳牛围栏

1、问题

2、分析

(1)暴力做法

看到这道题以后,我们可以先想一个最暴力的做法,就是我们去枚举所有长度至少为 F F F的区间,然后求出这个区间的和,再求出这个区间的平均值。最后在这些平均值之间取一个最大值。

那么这个暴力做法的时间复杂度是多少呢?枚举所有符合长度要求的区间,该过程在最坏条件下的复杂度是 O ( n 2 ) O(n^2) O(n2),求出区间的和,复杂度是 O ( n ) O(n) O(n)。那么总的时间复杂度就是 O ( n 3 ) O(n^3) O(n3)。

很明显这个做法是超时的,那么对于这个暴力的做法,我们可以给出一个小小的优化,即我们通过前缀和算法将求区间的时间复杂度优化到 O ( 1 ) O(1) O(1),优化后,该做法的时间复杂度是 O ( n 2 ) O(n^2) O(n2)。优化后依旧是超时的。

(2)二分答案+前缀和+DP

受朴素做法的启发,我们先求一下前缀和,将前缀和数组记作: s i si si

这道题我们可以换个思路想,假设给你一个平均值 x x x。我们要做的是判断这个平均值是否能够达到。

换句话说,我们就是要判断是否存在一个区间,使得这个区间和的平均值达到了 x x x。

将其转化为数学表达式即:

是否存在一个区间 i , j i,j i,j,其中 i − j + 1 > = F i-j+1>=F i−j+1>=F,满足:
s i − s j − 1 i − j + 1 ≥ x \frac{si - sj - 1}{i-j+1} \geq x i−j+1si−sj−1≥x

等价于
s i − s j − 1 ≥ x ∗ ( i − j + 1 ) si - sj - 1 \geq x * (i-j+1) si−sj−1≥x∗(i−j+1)

等价于
a j + a j + 1 + . . . + a i ≥ x + x + . . . + x aj+aj+1+...+ai\geq x+x+...+x aj+aj+1+...+ai≥x+x+...+x

等价于
( a j − x ) + ( a j + 1 − x ) + . . . + ( a i − x ) ≥ 0 (aj-x)+(aj+1-x)+...+(ai-x)\geq 0 (aj−x)+(aj+1−x)+...+(ai−x)≥0

如果我们设 b i = a i − x bi=ai-x bi=ai−x, b i bi bi的前缀和数组为 T i Ti Ti的话,

上述式子即可转化为:
T i − T j − 1 ≥ 0 Ti-Tj-1\geq0 Ti−Tj−1≥0

如果想要找到这样一个合法区间,我们只需要找到一个平均值最大的区间,看看它是否到达 x x x即可。

因此,我们可以枚举区间右端点 i i i,即固定 T i Ti Ti,要想让 T i − T j − 1 Ti-Tj-1 Ti−Tj−1最大,只要找到最小的 T j − 1 Tj-1 Tj−1即可。该过程可以用 D P DP DP来做。

这个 D P DP DP比较简单,定义 d p i dpi dpi为前 i i i个 T i Ti Ti中最小的一个。

转移方程为: d p i = m i n ( d p i − 1 , T i ) dpi = min(dpi-1,Ti) dpi=min(dpi−1,Ti)

那么我们的 T i − T j − 1 Ti-Tj-1 Ti−Tj−1的最大值即: T i − d p i − F Ti-dpi-F Ti−dpi−F

因为我们是枚举的 i i i,所以这个过程是 O ( n ) O(n) O(n)的。

也就是说我们只需要通过 O ( n ) O(n) O(n)的时间复杂度,就能够判断一个平均值 x x x是否能够达到。

因此,我们只需要去二分这个 x x x即可,即二分答案。

而通过题目,我们发现, x x x的最大值就是2000。

因此,总的时间复杂度就是: O ( n l o g ( 2000 ) ) O(nlog(2000)) O(nlog(2000))

3、代码

cpp 复制代码
#include<bits/stdc++.h>
#define endl '\n'
#define deb(x) cout << #x << " = " << x << '\n';
#define INF 0x3f3f3f3f
using namespace std;
const int N = 1e5 + 10;
const double eps = 1e-5;
int n, f;
double a[N], T[N], dp[N];

bool check(double mid)
{
	for(int i = 1; i <= n; i ++)
		T[i] = T[i - 1] + a[i] - mid;

	for(int i = 1; i <= n; i ++)
	{
	    dp[i] = min(dp[i - 1], T[i]);
	}
	
	for(int i = f; i <= n; i ++)
	{
		if(T[i] - dp[i-f] >= 0)
			return true;
	}
	return false;
}

void solve()
{
	cin >> n >> f;
	for(int i = 1; i <= n; i ++)
		cin >> a[i];

	double l = 0, r = 2000.0;
	while(r - l > eps)
	{
		double mid = (l + r) / 2.0;
		if(check(mid))
			l = mid;
		else
			r = mid;
	}	
	cout << (int)(r * 1000) << endl;
}

signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t = 1;
	//cin >> t;
	while(t--)
	solve();
}
相关推荐
兰令水3 分钟前
leecodecode【面试150】【2026.6.15打卡-java版本】
java·算法·面试
WWW652621 分钟前
代码随想录 打卡第五十八天
开发语言·c++·算法
pen-ai23 分钟前
【HistGBM 系列①】从决策树到梯度提升 —— GBDT 原理精讲
算法·决策树·机器学习
Black蜡笔小新32 分钟前
零代码私有化自动化AI算法训练服务器DLTM如何破解企业AI落地难题
人工智能·算法·自动化
liulilittle44 分钟前
回归物理本质:对拥塞控制实验室依赖与公平性误置的反思
网络·tcp/ip·计算机网络·算法·tcp·通信·拥塞控制
牛油果子哥q44 分钟前
unordered_set / unordered_map 底层哈希表精讲,哈希原理、哈希冲突、链地址法、源码结构、有序与无序容器终极选型全解
数据结构·算法·哈希算法·散列表
进击的荆棘1 小时前
优选算法——优先级队列
数据结构·c++·算法·leetcode·优先级队列
wen_zhufeng1 小时前
AudioX\-Turbo:面向通用音频生成的高效多模态统一框架
人工智能·算法·音视频
牛油果子哥q1 小时前
哈希表经典刷题模型与布隆过滤器精讲,哈希查重、哈希计数、双哈希映射、误判原理与工业级落地应用
数据结构·算法·哈希算法·散列表
ruxshui1 小时前
排序算法及不同场景应用总结
算法·排序算法