【记录】四道双指针

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


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

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

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


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; 
}
相关推荐
WL_Aurora2 分钟前
Python 算法基础篇之排序算法(二):希尔、快速、归并
python·算法·排序算法
闻缺陷则喜何志丹7 分钟前
【图论 树 启发式合并】P7165 [COCI2020-2021#1] Papričice|普及+
c++·算法·启发式算法·图论··洛谷
alexwang2119 分钟前
AT_abc458_d [ABC458D] Chalkboard Median题解
c++·算法·题解·atcoder
故事和你919 分钟前
洛谷-【图论2-4】连通性问题1
开发语言·数据结构·c++·算法·动态规划·图论
我先去打把游戏先17 分钟前
Ubuntu虚拟机(服务器版本)Git安装教程(附常用命令)——从零开始掌握版本控制
服务器·c语言·c++·git·嵌入式硬件·物联网·ubuntu
周末也要写八哥19 分钟前
算法实例分析:使数组相等的最小开销
算法
吃好睡好便好22 分钟前
在Matlab中绘制质点运动轨迹图
开发语言·学习·算法·matlab·信息可视化
艾莉丝努力练剑23 分钟前
【Linux网络】Linux 网络编程:HTTP(四)从手写服务器到生产级 Nginx 与 cpp-httplib 实战
linux·运维·服务器·网络·c++·nginx·http
爱炼丹的James25 分钟前
第三章 搜索和图论
数据结构·算法·图论
菜菜笔记26 分钟前
【无标题】
算法