单调队列(专项复习)

P1886 滑动窗口 /【模板】单调队列

 

 题目思路:单调队列模版题,每次都输出队列中的第一个元素。以此维护队列中元素序号的单调性,得到结果。只是比较判断,时间复杂度很小。相等的值也要挤掉。最大值同理。

代码实现:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 3000005
ll dp[N], f1[N], f2[N];
ll a[N], b[N], c[N];
bool vis[N], flag;
ll x, y, z, n, m, t, k;
ll ans, sum, cnt;
deque<ll>q;
int main() 
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	for (int i = 1; i <= n; i++) {
		while (!q.empty() && a[q.back()] >= a[i]) q.pop_back();
		q.push_back(i);
		if (i >= m) {
			while (q.front() <= i - m) q.pop_front();
			cout << a[q.front()] << " ";
		}
	}
	cout << endl;
	q.clear();
	for (int i = 1; i <= n; i++) {
		while (!q.empty() && a[q.back()] <= a[i]) q.pop_back();
		q.push_back(i);
		if (i >= m) {
			while (q.front() <= i - m) q.pop_front();
			cout << a[q.front()] << " ";
		}
	}
	return 0;
}

P1901 发射站

题目思路: 用一个线性算法,先从左往右推。若在栈中栈顶那个塔的高度比当前塔的高度低,即此塔为栈顶塔右边的第一个比栈顶塔高的塔,那么栈顶塔的能量就要加给当前塔,然后将队列顶塔出栈。直到队列内无塔或下一塔比它的高度高,就将该塔进队列,并指向下一个塔。反之亦然。两趟之间要清空栈,最后再打个擂台,找MAX。

代码实现:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 3000005
ll dp[N], f1[N], f2[N];
ll a[N], b[N], c[N];
bool vis[N], flag;
ll x, y, z, n, m, t, k;
ll ans, sum, cnt;
deque<ll>q;
struct node {
	ll x, y, z;
}f[N];
int main()
{
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> f[i].x >> f[i].y;
	for (int i = 1; i <= n; i++) {
		while (!q.empty() && f[q.back()].x < f[i].x) {
			f[i].z += f[q.back()].y;
			q.pop_back();
		}
		if (!q.empty()) {
			f[q.back()].z += f[i].y;
		}
		q.push_back(i);
	}
	for (int i = 1; i <= n; i++)
		ans = max(ans, f[i].z);
	cout << ans << endl;
	return 0;
}

P1419 寻找段落

题目思路:

首先看出答案满足单调性,可以二分答案,转化为判定性问题。令最优平均值为k,对于所有合法序列[l..r],尝试将分式变为整式:s[l..r]>k*(r-l+1)无解。移项,令b[i]=a[i]-k,s’[i]为b[1]..b[n]的和,则有寻找长度在[s,t]内的子段和的最大值可以用单调队列解决

代码实现:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 3000005
ll dp[N], f1[N], f2[N], a[N];
double b[N], c[N];
bool vis[N], flag;
ll x, y, z, n, m, t, k;
ll sum, cnt;
double l, r, mid, ans;
deque<ll>q;

bool check(double k) {
	q.clear();
	for (int i = 1; i <= n; i++)
		c[i] = (double)a[i] - k;
	for (int i = 1; i <= n; i++) {
		b[i] = b[i - 1] + c[i];
	}
	for (int i = 1; i <= n; i++) {
		if (i >= x) {
			while (!q.empty() && b[q.back()] > b[i - x]) q.pop_back();
			q.push_back(i - x);
		}
		if (!q.empty() && i - y > q.front()) q.pop_front();
		if (!q.empty() && b[i] - b[q.front()] >= 0) return true;
	}
	return false;
}
int main()
{
	cin >> n >> x >> y;
	for (int i = 1; i <= n; i++)
		cin >> a[i];
	l = -10000; r = 10000;
	while (r - l > 1e-5) {
		mid = (l + r) /2;
		if (check(mid))
			ans = l = mid;
		else
			r = mid;
	}
	printf("%.3lf\n", ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值