用两个有序区间指针逐段求交,推进结束时刻更早的一方。
OJ: shumeng
题目 ID: CSP201809B
难度:入门
标签:双指针区间模拟
日期: 2026-07-31 16:21
形式化题目
给定两组互不重叠且按时间递增排列的闭区间
思路
朴素做法
小数据可以枚举第一人的每一段与第二人的每一段,两段相交时累加交集长度
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-31 16:21
* update_at: 2026-08-17 22:41
*/
// brute.cpp:小数据暴力解,枚举两人的每一对装车区间,累加交集长度。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2005;
int n;
int left_h[MAXN], right_h[MAXN]; // 小 H 的装车时间段
int left_w[MAXN], right_w[MAXN]; // 小 W 的装车时间段
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> left_h[i] >> right_h[i];
}
for (int i = 1; i <= n; i++) {
cin >> left_w[i] >> right_w[i];
}
// 枚举所有区间对,交集长度取 max(0, min(r1,r2) - max(l1,l2))。
int answer = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
int overlap_left = max(left_h[i], left_w[j]);
int overlap_right = min(right_h[i], right_w[j]);
if (overlap_left < overlap_right) {
answer += overlap_right - overlap_left;
}
}
}
cout << answer << '\n';
return 0;
}双指针线性推进
因为两组区间内部都有序且互不重叠,只需维护当前的小 H 段
- 先累加这两段的交集长度。
- 结束时刻更早的区间不可能再和对方后续任何区间相交,因此推进它的指针。
当结束时刻相等时,推进任意一方都正确,代码约定推进第二个指针。
代码
cpp
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-07-31 16:21
* update_at: 2026-08-17 22:41
*/
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2005;
int n;
int left_h[MAXN], right_h[MAXN]; // 小 H 的装车时间段 [left_h[i], right_h[i]]
int left_w[MAXN], right_w[MAXN]; // 小 W 的装车时间段 [left_w[i], right_w[i]]
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> left_h[i] >> right_h[i];
}
for (int i = 1; i <= n; i++) {
cin >> left_w[i] >> right_w[i];
}
// 两段区间 [l1,r1] 与 [l2,r2] 的交集长度为 max(l1,l2)..min(r1,r2) 的长度。
int answer = 0;
int i = 1, j = 1;
while (i <= n && j <= n) {
int overlap_left = max(left_h[i], left_w[j]);
int overlap_right = min(right_h[i], right_w[j]);
if (overlap_left < overlap_right) {
answer += overlap_right - overlap_left;
}
// 结束时刻更早的一方不可能再和对方的后续区间相交,推进它。
if (right_h[i] < right_w[j]) {
i++;
} else {
j++;
}
}
cout << answer << '\n';
return 0;
}复杂度
- 时间:两个指针各至多前进
次, 。 - 空间:保存两组区间,
。
总结
处理两组有序、不重叠的区间时,不必枚举所有区间对。当前两段比较后,结束更早的一段已没有后续机会,这正是双指针能线性推进的原因。