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();
}
相关推荐
小李飞刀李寻欢1 小时前
DeepSeek V3 版本模型结构分析
算法·大模型·deepseek
某不知名網友1 小时前
C++ 七大排序算法完整讲解
java·算法·排序算法
得物技术1 小时前
得物推荐系统诊断 Agent:从 “调接口” 到 “会思考”|AICon 演讲整理
人工智能·算法·架构
Lugas2 小时前
为啥说男生找对象尽量在25岁前找到?
算法
MrZhao4002 小时前
从能跑到可用:一个 Agent Harness 还差哪些工程闭环?
算法
QN1幻化引擎2 小时前
Gravity-Anchored Cognitive Field Architecture: The DalinX V8/V10 Implementation
java·前端·算法
学计算机的计算基3 小时前
LeetCode 图论四题精讲:BFS、拓扑排序、Trie 树的模板与优化
java·笔记·算法
浩瀚地学3 小时前
【面试算法笔记】0202-链表-基本功能实现
java·经验分享·笔记·算法·面试
tkevinjd3 小时前
416分割等和子集
java·python·算法·leetcode·职场和发展
Keven_113 小时前
算法札记:Tarjan与拓扑序(Topo)的关系
算法·拓扑·tarjan