单调队列的运用

64 篇文章 0 订阅
本文介绍了如何使用单调队列解决两道编程竞赛题目,分别是[HAOI2007]理想的正方形和选择数字。在第一题中,通过维护行和列的最大值和最小值,利用单调队列压缩矩阵并求解最大差值。第二题中,利用单调队列优化动态规划,求解在限制连续取数的情况下,删除若干数的最小和。两题都展示了单调队列在处理区间最值问题上的高效性。
摘要由CSDN通过智能技术生成

[HAOI2007]理想的正方形 - 洛谷https://www.luogu.com.cn/problem/P2216首先这一题我们可以压缩矩阵,用单调队列维护行和列

先维护每一行区间最大值和最小值

再维护更新后的数组的每一列的最大值和最小值

最后得出的数组的每一个元素就是n*n矩阵的元素的最大值和最小值,画图就可以明白了、

int s[1005][1005];
int X[1005][1005], x[1005][1005];
int Y[1005][1005], y[1005][1005];
//int q1[MAXN];
//int q2[MAXN];
int main() {
	deque<int> q1, q2;
	int a, b, n;
	scanf("%d %d %d", &a, &b, &n);
	for (int i = 1; i <= a; i++) {
		for (int j = 1; j <= b; j++) {
			scanf("%d", &s[i][j]);
		}
	}
	for (int i = 1; i <= a; i++) {
		for (int j = 1; j <= b; j++) {
			if (!q1.empty() && j - q1.front() >= n)
				q1.pop_front();
			if (!q2.empty() && j - q2.front() >= n)
				q2.pop_front();
			while (!q1.empty() && s[i][q1.back()] < s[i][j]) {
				q1.pop_back();
			}
			while (!q2.empty() && s[i][q2.back()] > s[i][j]) {
				q2.pop_back();
			}
			q1.push_back(j);
			q2.push_back(j);
			if (j >= n) {
				X[i][j - n + 1] = s[i][q1.front()];
				x[i][j - n + 1] = s[i][q2.front()];
			}
		}
		q1.clear();
		q2.clear();
	}
	for (int i = 1; i <= b - n + 1; i++) {
		for (int j = 1; j <= a; j++) {
			if (!q1.empty() && j - q1.front() >= n)
				q1.pop_front();
			if (!q2.empty() && j - q2.front() >= n)
				q2.pop_front();
			while (!q1.empty() && X[q1.back()][i] < X[j][i])
				q1.pop_back();
			while (!q2.empty() && x[q2.back()][i] > x[j][i])
				q2.pop_back();
			q1.push_back(j);
			q2.push_back(j);
			if (j >= n) {
				Y[j - n + 1][i] = X[q1.front()][i];
				y[j - n + 1][i] = x[q2.front()][i];
			}
		}
		q1.clear();
		q2.clear();
	}
	int ans = INF;
	for (int i = 1; i <= a - n + 1; i++) {
		for (int j = 1; j <= b - n + 1; j++) {
			ans = min(ans, Y[i][j] - y[i][j]);
		}
	}
	printf("%d", ans);
	return 0;
}

选择数字 - 洛谷https://www.luogu.com.cn/problem/P2034这一题属于单调队列优化的dp

题目要求我们求取若干数,但不连续取k个以上的数后的最大和。我们可以将问题转换为删除若干数,且删除数的间隔最多为k,求删除数的最小和。

所以我们易得

dp[i]=a[i]  i<=k+1;

dp[i]=min dp[j]+ a[i]   i>k+1

维护第二个递推式时我们需要用到单调队列维护,维护k+1区间的dp的最小值;

int n, k;
ll a[MAXN];
ll dp[MAXN];
int main() {
	scanf("%d %d", &n, &k);
	ll sum = 0;
	for (int i = 1; i <= n; i++) {
		scanf("%lld", a + i);
		sum += a[i];
	}
	deque<int> q;
	for (int i = 1; i <= n; i++) {
		if (i <= k + 1)
			dp[i] = a[i];
		else
			dp[i] = dp[q.front()] + a[i];
		if (!q.empty() && i - q.front() >= k + 1)
			q.pop_front();
		while (!q.empty() && dp[q.back()] > dp[i])
			q.pop_back();
		q.push_back(i);
	}
	printf("%lld\n", sum - *min_element(dp + n - k, dp + n + 1));
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值