[USACO12MAR] Flowerpot S题解(单调队列 c++)
题目链接:[USACO2012-Mar-Silver] Flowerpot
题意:
- 给你n个点,每个点有对应的x,y
- 确认是否存在两个点,在 y 1 , y 2 y_1,y_2 y1,y2满足要求的情况下,输出最小的 ∣ x 2 − x 1 ∣ \lvert x_2 - x_1 \rvert ∣x2−x1∣
思路:
- 如果暴力的话,我们就考虑对于每一个点,寻找其对应的 ∣ y 1 − y 2 ∣ > = D \lvert y_1-y_2 \rvert >= D ∣y1−y2∣>=D 的所有点,然后找最小的 ∣ x 2 − x 1 ∣ \lvert x_2 - x_1 \rvert ∣x2−x1∣
#include <iostream>
#include <vector>
#include <deque>
#include <algorithm>
using namespace std;
int ans = 0x3f3f3f3f;
int main() {
int n, d;
cin >> n >> d;
vector<vector<int>> v(n, vector<int>(2));
for (int i = 0; i < n; i ++)
cin >> v[i][0] >> v[i][1];
sort(v.begin(), v.end());
for (int i = 0; i < n; i ++)
{
for (int j = i + 1; j < n; j ++) {
if (v[j][1] - v[i][1] >= d)
ans = min(ans, v[j][0] - v[i][0]);
}
}
if (ans == 0x3f3f3f3f)
cout << -1;
else
cout << ans;
return 0;
}
- 暴力的时间复杂度为 O ( n 2 ) O(n^2) O(n2)。
- 不如我们这样想:首先把这些点按x坐标进行排序,令 l l l在这些点中进行遍历,对于每一个 l ∈ ( 1 , n ) l \in (1,n) l∈(1,n)我们都可以求出最小的 r r r,使得刚好在 [ l , r ] [l,r] [l,r]区间内恰好存在 ∣ y 1 − y 2 ∣ > = D \lvert y_1-y_2 \rvert >= D ∣y1−y2∣>=D的情况(区间 [ l , r − 1 ] [l,r-1] [l,r−1]就不存在)
- 这样问题就成了一个大小不固定的滑动窗口问题。我们使用队列
q1
的头结点存储从a[l].x
开始y
最大的值,q2
存储最小值。 - 以
q1
为例,若a[i+1]>a[i]
则a[i]
没意义的。因为当r=i时还没有满足 ∣ y 1 − y 2 ∣ > = D \lvert y_1-y_2 \rvert >= D ∣y1−y2∣>=D。若$ y_{i+1}-y_j >= D(j<i+1) , 显然 a [ i ] 无意义,若 ,显然a[i]无意义,若 ,显然a[i]无意义,若 y_{i+1}-y_j >= D(j>i+1) , 我们求最小的 ,我们求最小的 ,我们求最小的\lvert x_2 - x_1 \rvert$,所以当a[i+1]在时,a[i]永无出头之日 - 我们通过这种方式,遍历出每一个l时[l,r]中符合条件的 ∣ x 2 − x 1 ∣ \lvert x_2 - x_1 \rvert ∣x2−x1∣
代码如下
#include <iostream>
#include <algorithm>
using namespace std;
typedef pair<int, int> PII;
const int N = 1e6 + 10;
PII a[N];
// q1维护最大值(递减) q中存储序号
int q1[N], q2[N];
int h1 = 1, h2 = 1, t1, t2;
int ans = 0x3f3f3f3f;
int main() {
int n, d;
cin >> n >> d;
for (int i = 1; i <= n; i ++)
cin >> a[i].first >> a[i].second;
sort(a + 1, a + 1 + n);
for (int l = 1, r = 0; l <= n; l ++) {
while (h1 <= t1 && q1[h1] < l) h1 ++;
while (h2 <= t2 && q2[h2] < l) h2 ++;
while (a[q1[h1]].second - a[q2[h2]].second < d && r < n) {
r ++;
while (h1 <= t1 && a[q1[t1]].second < a[r].second) t1 --;
q1[++ t1] = r;
while (h2 <= t2 && a[q2[t2]].second > a[r].second) t2 --;
q2[++ t2] = r;
}
if (a[q1[h1]].second - a[q2[h2]].second >= d)
ans = min(ans, abs(a[q1[h1]].first - a[q2[h2]].first));
}
if (ans == 0x3f3f3f3f)
cout << -1;
else
cout << ans;
return 0;
}
这道题想了很长时间,如有讲的不清楚的地方,恳请大家批评指正