P1419 寻找段落 【二分 + 单调队列 + 前缀和】

给定一个序列,定义段落为长度在[S,T]之间的连续序列,目标是找到平均值最大的段落。通过二分法与单调队列,维护长度为S-T的段落和的最大值来解决此问题。注意区间长度计算的细节,避免低级错误。理解算法思想比直接看代码更重要。" 123085464,10360630,使用pytest-allure生成测试报告,"['测试用例', 'python', 'pytest', 'allure', '测试框架']
摘要由CSDN通过智能技术生成

题目描述

给定一个长度为n的序列a_i,定义a[i]为第i个元素的价值。现在需要找出序列中最有价值的“段落”。段落的定义是长度在[S,T]之间的连续序列。最有价值段落是指平均值最大的段落,

段落的平均值=段落总价值/段落长度。
输入格式

第一行一个整数n,表示序列长度。

第二行两个整数S和T,表示段落长度的范围,在[S,T]之间。

第三行到第n+2行,每行一个整数表示每个元素的价值指数。
输出格式

一个实数,保留3位小数,表示最优段落的平均值。
输入输出样例
输入 #1

3

2 2

3

-1

2

输出 #1

1.000

说明/提示

【数据范围】

对于30%的数据有n<=1000。

对于100%的数据有n<=100000,1<=S<=T<=n,-10000<=价值指数<=10000。

【题目来源】

tinylic改编

还是那句话,一个bug越难找,往往这个bug就越低级。
区间长度为s的段落数之和我把sum[i] - sum[i - s] 写成了sum[i] - sum[i - s + 1]…找了好久的bug。。。
这道题关键是用单调队列维护一个长度为 s - t 的段落和的最大值。
看别人代码一直没看懂,,这时候电脑没电了给电脑充电的过程中思考如何维护段落的最大值,然后想明白了,也敲出来了。。何必看之前看别人代码呢?
算法,重要的是算法思想,而非代码本身。

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;

const int maxn = 1e5 + 10;
int a[maxn];
int que[maxn];
double sum[maxn];
int head, tail;
int n, m;
int s, t;

bool judge(double x) {
	head = 0, tail = 0;
//	cout << x << endl;
	for(int i = 1; i <= n; i++)
		sum[i] = sum[i - 1] + (double)a[i] - x;
	for(int i = s; i <= n; i++) {
		while(head < tail && que[head] <= i - t)	head++;
		while(head < tail && sum[i - s] <= sum[que[tail - 1]])	tail--;
		que[tail++] = i - s;
		if(head < tail &&  sum[i] - sum[que[head]] >= -1e-5)
		{
	//	  cout << i << " " << que[head] << " " << sum[i] << " " << sum[que[head]] << endl;
			return true;
		}
	}
	return false;
}

int main() {
	cin >> n >> s >> t;
	for(int i = 1; i <= n; i++)	cin >> a[i];
	double l = -100000, r = 100000;
	double ans = 0;
//	cout << judge( 1.52588) << endl;
	while(r - l > 1e-5) {
		double mid = (l + r) / 2;
	//	cout << judge(mid) << " " << l << " " << r << " " << mid << endl;
		judge(mid) ? ans = mid, l = mid : r = mid;	
	}
	printf("%.3lf", ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值