【记录】四道双指针

教练给了四道双指针,品鉴中。


双指针通过两个指针的协同工作,避免不必要的重复遍历,从而提高算法效率。

时间复杂度一般在 左右,分为同向移动和双向移动两种。

大多都是在维护一个区间,要求这个区间满足某种性质,且包含的元素最少(或求可能的个数)。


1.[USACO16OPEN] Diamond Collector S

P3143 [USACO16OPEN] Diamond Collector S - 洛谷 (luogu.com.cn)

排序是显然的,在排好序的数列中找出两段长度和最大的不重合的区间,并使两个区间中的最大值与最小值的差不大于 k。

可以枚举一个分割点 分割两端区间,定义 l 和 r 作为 两端区间的起始和结尾。

定义 mx 为 点左边(不包含 )最多可以取多少点,每次更新答案就是 mx + r - i + 1。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N = 5e4 + 10;

LL a[N];

int main () {
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	int n; LL K;
	cin >> n >> K;
	for (int i = 1; i <= n; i ++) {
		cin >> a[i];
	}
	sort (a + 1, a + n + 1);
	
	LL mx = 1, ans = 1; int l = 1, r = 2;
	for (int i = 2; i <= n; i ++) {
		while (a[i - 1] - a[l] > K && l < n) {
			l ++;
		}
		while (a[r] - a[i] <= K && r < n) {
			r ++;
		}
		if (a[r] - a[i] > K) {
			r --;
		}
		mx = max(mx, 1ll * i - 1 - l + 1);
		ans = max(ans, mx + r - i + 1);
	}
	cout << ans << "\n";
	
	return 0;
}

2.单词背诵

P1381 单词背诵 - 洛谷 (luogu.com.cn)

要维护一个包含最多要背单词的最短区间。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10;

int a[N];
map<string, int> mpa;
map<string, int> mpb;
map<int, int> mp;

int main () {
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	int n;
	cin >> n;
	int cnta = 0;
	for (int i = 1; i <= n; i ++) {
		string s;
		cin >> s;
		if (!mpa[s]) {
			cnta ++;
			mpa[s] = cnta;
		}
	}
	
	int m;
	cin >> m;
	memset(a, 0, sizeof(a));
	int cntb = 0;
	for (int i = 1; i <= m; i ++) {
		string s;
		cin >> s;
		if (mpa[s]) {
			if (!mpb[s]) {
				cntb ++;
				mpb[s] = cntb;
			}
			a[i] = mpb[s];
		}
	}
	cout << cntb << "\n";
	if (cntb == 0) {
		cout << "0\n";
		return 0;
	}
	
	int sum = 0, ans = 1e9;
	for (int i = 1, j = 0; i <= m; i ++) {
		while (sum < cntb && j < m) {
			j ++;
			if (a[j] != 0) {
				if (!mp[a[j]]) {
					sum ++;
				}
				mp[a[j]] ++;
			}
		}
		if (sum == cntb) {
			ans = min(ans, j - i + 1);
		}
		if (mp[a[i]]) {
			mp[a[i]] --;
			if (!mp[a[i]]) {
				sum --;
			}
		}
	}
	cout << ans << "\n";
	
	return 0;
}

3.逛画展

P1638 逛画展 - 洛谷 (luogu.com.cn)

要维护一个包含所有大师画的最短区间。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

const int N = 1e6 + 10;
int a[N];
map<int, int> mp;

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	int n, m;
	cin >> n >> m;
	for (int i = 1; i <= n; i ++ ) {
		cin >> a[i];
	}
	
	int cnt = 0, st = 1e9, ed = 1e9, len = 1e9;
	for (int i = 1, j = 0; i <= n; i ++) {
		while (cnt < m && j < n) {
			j ++;
			if (!mp[a[j]]) {
				cnt ++;
			}
			mp[a[j]] ++;
		}
		if (cnt == m && j - i + 1 < len) {
			st = i;
			ed = j;
			len = j - i + 1;
		}
		mp[a[i]] --;
		if (!mp[a[i]]) {
			cnt --;
		}
	}
	cout << st << " " << ed << "\n";
	
	return 0; 
}

4.[ABC098D] Xor Sum 2

AT_arc098_b [ABC098D] Xor Sum 2 - 洛谷 (luogu.com.cn)

注意到窗口内所有数的二进制表示中,每一位最多只有一个数在该位上是 1。

因为当两个数在同一个二进制位上都是 1 时,加法会产生进位,而异或不会。

所以满足题目要求的一段中的任何一段都是满足要求的。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;

typedef long long LL;
const int N = 2e5 + 10;
LL a[N];

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	int n;
	cin >> n;
	for (int i = 1; i <= n; i ++ ) {
		cin >> a[i];
	}
	
	LL ans = 0;
	LL sum = 0, res = 0;
	for (int i = 1, j = 0; i <= n; i ++) {
		while (sum == res && j < n) {
			j ++;
			sum += a[j];
			res ^= a[j];
		}
		if (sum != res) {
			sum -= a[j];
			res ^= a[j];
			j --;
		}
		ans += (j - i + 1);
		sum -= a[i];
		res ^= a[i];
	}
	cout << ans << "\n";
	
	return 0; 
}
相关推荐
_OP_CHEN1 小时前
算法基础篇:(十二)基础算法之倍增思想:从快速幂到大数据运算优化
大数据·c++·算法·acm·算法竞赛·倍增思想
Murphy_lx1 小时前
C++ 条件变量
linux·开发语言·c++
xie0510_1 小时前
C++入门
c++
AA陈超1 小时前
ASC学习笔记0027:直接设置属性的基础值,而不会影响当前正在生效的任何修饰符(Modifiers)
c++·笔记·学习·ue5·虚幻引擎
羚羊角uou1 小时前
【C++】智能指针
开发语言·c++
杜子不疼.1 小时前
【C++】哈希表基础:开放定址法 & 什么是哈希冲突?
c++·哈希算法·散列表
CoovallyAIHub1 小时前
分割万事万物的AI,再进化!Meta SAM 3 来了,支持中文提示词!
深度学习·算法·计算机视觉
九年义务漏网鲨鱼1 小时前
蓝桥杯算法——记忆化搜索
算法·职场和发展·蓝桥杯
04aaaze1 小时前
C++(C转C++)
c语言·c++·算法