游戏-单调队列

发布于:2025-06-09 ⋅ 阅读:(15) ⋅ 点赞:(0)

问题描述

熊大和熊二在玩游戏。他们将 n 个正整数 a1,a2,...,an​ 排成一行,然后各用一个长度为 k 的框在这个数组中各自随机框选出一段长度为 k 的连续子序列(随机框选指在合法的 nk+1 个连续子序列中均匀随机)。熊大记录了他框出的 k 个数中的最大值 P,熊二记录了他框出的 k 个数的最小值 Q,他们突然有个疑问:PQ 的期望是多少?

输入描述

输入共 2 行。

第一行为两个正整数 n,k

第二行为 n 个由空格隔开的正整数 a1​,a2​,...,an​。

输出描述

输出共 1 行,一个浮点数(请保留两位小数)。

样例输入

3 2
1 2 3

样例输出

1.00

样例说明

一共有四种情况:

熊大框出 [1,2],P=2;熊二框出 [1,2],Q=1,PQ=1。

熊大框出 [1,2],P=2;熊二框出 [2,3],Q=2,PQ=0。

熊大框出 [2,3],P=3;熊二框出 [1,2],Q=1,PQ=2。

熊大框出 [2,3],P=3;熊二框出 [2,3],Q=2,PQ=1。

所以 PQ 的期望为(1+0+2+1)/4=1.00。

评测用例规模

对于 20% 的数据,保证 n≤102。

对于 40%40% 的数据,保证 n≤103。

对于 100% 的数据,保证 n≤105,0<ai≤109,0<k≤n。

运行限制

语言 最大运行时间 最大运行内存
C++ 1s 256M
C 1s 256M
Java 1s 256M
Python3 3s 256M
PyPy3 3s 256M
Go 3s 256M
JavaScript 3s 256M

 解析:

要找出长度为k的框中的最大最小数字,相当于采用滑动窗口,从第一个开始一个一个的移动窗口,维护两个队列,看当前窗口中的数的最大最小。从题目中可以看出求最大-最小的平均值,相当于求最大值的和-最小值的和除以2.

找最大数:

 维护一个滑动窗口大小为k,一个递减队列存储序号。

首先处理队列中的序号是否在当前滑动窗口中,查看队首序号如果在窗口中不处理,否则弹出队列,继续查看。

然后处理新插入的数字,如果当前插入的数比队尾的数大,将队尾弹出,继续判断,因为要维护一个递减队列,队首最大,队尾最小。

将每一个滑动窗口队首元素求和

找最小数:

 维护一个滑动窗口大小为k,一个递增队列存储序号。

首先处理队列中的序号是否在当前滑动窗口中,查看队首序号如果在窗口中不处理,否则弹出队列,继续查看。

然后处理新插入的数字,如果当前插入的数比队尾的数小,将队尾弹出,继续判断,因为要维护一个递增队列,队首最小,队尾最大。

将每一个滑动窗口队首元素求和

求值:

n个数字可以正好划分n-k+1框

(最大和-最小和)/(n-k+1)

#include<iostream>
#include<string>
#include<cstring>
#include<deque>
using namespace std;
typedef long long ll;
const ll MAX = 1e5 + 4;
ll a[MAX];
deque<int> dqmax, dqmin;//存储序号
ll dmax = 0, dmin = 0;
int main()
{
	ll n, k;
	cin >> n >> k;
	for (int i = 1; i <= n; i++)
	{
		cin >> a[i];
	}
	for (int i = 1; i <= n; i++)
	{
		//先看滑动窗口   离开滑动窗口的弹出队列
		while (!dqmax.empty() && dqmax.front() < i - k + 1)
		{
			dqmax.pop_front();
		}
		//递减队列  将小于a[i]弹出
		while (!dqmax.empty() && a[dqmax.back()] < a[i])
		{
			dqmax.pop_back();
		}
		dqmax.push_back(i);



		//先看滑动窗口   离开滑动窗口的弹出队列
		while (!dqmin.empty() && dqmin.front() < i - k + 1)
		{
			dqmin.pop_front();
		}
		//递增队列  将大于a[i]弹出
		while (!dqmin.empty() && a[dqmin.back()] > a[i])
		{
			dqmin.pop_back();
		}
		dqmin.push_back(i);


		//维护一个完整的滑动窗口i表示结束位  例如k=2时  i==2时才正好第一个滑动窗口
		if (i >= k)
		{
			dmax += a[dqmax.front()];
			dmin += a[dqmin.front()];
		}
	}
	printf("%.2f",1.0 * (dmax - dmin) / (n - k + 1));
	return 0;
}