P1638 逛画展 (双指针

文章讨论了一道C++编程问题,涉及到数组处理和算法设计。作者最初尝试从两端同时枚举,但发现对于某些情况如12222211111,该方法无法得到正确答案。于是调整策略,改为每次增加一个元素到计数器,当计数器等于目标值时,从左侧开始减少超出的元素。这个优化后的枚举方法能有效避免错误答案。
摘要由CSDN通过智能技术生成
#include <bits/stdc++.h>
using namespace std;
int n, m;
int a[1000100];
int b[1000100];
int ans = 2e9 + 1;
int rr, ll;
int ct;

int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}

	int j = 1;
	for (int i = 1; i <= n; i++) {
		b[a[i]]++;
		if (b[a[i]] == 1)
			ct++;
		if (ct == m) {
			while (j < i && b[a[j]] >= 2) {
				b[a[j]]--;
				j++;
			}
			if (i - j + 1 < ans) {
				ans = i - j + 1;
				rr = i;
				ll = j;
			}
			b[a[j]]--;
			ct--;
			j++;
		}
	}

	cout << ll << " " << rr;

}

本以为是一道水题,没想到调了这么久emmm。

首先一开始 想的是从两头枚举, 直到一端枚举完,在枚举另一端

但仔细想想就不对啦 比如  12222211111  ,明显答案是 中间那个2 1 

如果从两头枚举会把1 给 枚举掉,肯定错了

——————————————————————————————————

所以就知道了正确的枚举,每次将一个画,加入b中,如果没看过就ct++;

当ct == m 时 ,就可以从左端开始减去 画画的数量,很显然只有2及2以上的画可以去掉,

移动玩 i 后 比大小统计答案。

然后这个地方很重要 (之前一直没在意)由于比完后是相当于 右指针 在 前j 个位置所能达到的最理想情况, 所以 要将 i 再后移一位,去找下面的情况 

#include <bits/stdc++.h>
using namespace std;
int n, m;
int a[1000100];
int b[1000100];
int ans = 2e9 + 1;
int rr, ll;
int ct;

int main() {
	cin >> n >> m;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}

	int j = 0;//因为下面的原因所以决定从0开始
	for (int i = 1; i <= n ; i++ ) {
		while (j <= n && ct < m) {
			j++;//先得保证每次j都得产生位移,不然下一次会在重复的地方多算一次
			if (b[a[j]] == 0)
				ct++;
			b[a[j]]++;
		}
		if (j <= n && j - i + 1 < ans) {
			rr = j, ll = i;
			ans = j - i + 1;
		}

		b[a[i]]--;//将第i个画减去
		if (b[a[i]] == 0)
			ct--;



	}
	cout << ll << " " << rr;

}

第一个答案是属于先一个个枚举每幅画,ct == m 时再从左端开始减去

第二个答案是属于先让 ct == m  ,然后从末尾开始枚举  减去一幅画

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值