1. 题目来源
2. 题目解析
本来以为是道水题…确实思维上很水,但是实现上踩坑频繁…
set<int>
维护木材长度即可,lower_bound()
查找长度最接近的木材,出货就 erase()
。但是,但是,但是,lower_bound()
可能会查到 S.end()
迭代器,且我们不能直接 erase()
迭代器…然后就疯狂报错了…
在出货的时候,一定要找清楚出货的到底是谁,即到底是哪个迭代器,因为集合非空,一定是有一个迭代器出货,且这个迭代器要被删除的。 在这就不建议使用 lower_bound()
查找得到的 k
迭代器了,因为它可能是 end()
。
之前是直接在 set<int>
中插入一个 -1e9、1e9
作为边界值,就不会出现那么多的边界情况,但是且不要忘记原生写法是怎么写的。
简单改造后即可 ac
,针对 k
是否等于 S.end()
做特判即可,这样就不会对 S.end()
解引用和 erase()
了。且 k
和 u
一定是相邻的,如果 k==S.end()
那么输出的一定是 u
,删除的也一定是 u
,且在此的 u
一定合法!
代码:
// https://www.luogu.com.cn/problem/P5250
#include <iostream>
#include <algorithm>
#include <set>
using namespace std;
int n;
set<int> S;
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i ++ ) {
int a, b;
scanf("%d%d", &a, &b);
if (a == 1) {
if (S.find(b) != S.end()) printf("Already Exist\n");
else S.insert(b);
}
else if (S.empty()) printf("Empty\n");
else {
set<int>::iterator k = S.lower_bound(b); // k 在此可能等于 S.end(),不能删除 S.end(),即不能直接删除 k,得转化为 u
set<int>::iterator u = k;
if (u != S.begin()) u -- ; // u 一定不为 S.end(),因为在此集合不为空,至少有一个值,当为 begin() 的时候其等于 k
if (k != S.end() && *k - b < b - *u) u = k; // 在此不能写为 *k - b > b - *u) k = u; 因为一开始可能 k==S.end() 最后直接 erase 的时候出错
printf("%d\n", *u);
S.erase(u);
}
}
return 0;
}
改造:
// https://www.luogu.com.cn/problem/P5250
#include <iostream>
#include <algorithm>
#include <set>
using namespace std;
int n;
set<int> S;
int main() {
scanf("%d", &n);
for (int i = 0; i < n; i ++ ) {
int a, b;
scanf("%d%d", &a, &b);
if (a == 1) {
if (S.find(b) != S.end()) printf("Already Exist\n");
else S.insert(b);
}
else if (S.empty()) printf("Empty\n");
else {
set<int>::iterator k = S.lower_bound(b);
set<int>::iterator u = k;
if (u != S.begin()) u -- ;
if (k != S.end() && *k - b >= b - *u) k = u; // 长度相等,取短者,u 为短者
if (k != S.end()) printf("%d\n", *k), S.erase(k);
else printf("%d\n", *u), S.erase(u);
}
}
return 0;
}