对CF1904C的代码优化(复用性、指针转下标)
分讨,然后 \(k = 2\) 的时候肯定要写暴力,但是我的暴力很不优雅。
石山
void solve() {
int n, k;
cin >> n >> k;
vector<ll> a(n + 1);
for (int i = 1; i <= n; i++) cin >> a[i];
if (k >= 3) {
cout << 0 << endl;
return ;
}
sort(a.begin() + 1, a.begin() + n + 1);
ll ans = a[1];
if (k == 1) {
for (int i = 2; i <= n; i++) {
ans = min(ans, a[i] - a[i - 1]);
}
cout << ans << endl;
}
else {
for (int i = 2; i <= n; i++) {
ans = min(ans, a[i] - a[i - 1]);
}
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
ll value = a[j] - a[i];
auto right_it = lower_bound(a.begin() + 1, a.begin() + n + 1, value);
if (right_it == a.begin() + n + 1) {
ans = min(ans, abs(a[n] - value));
}
if (*right_it == value) {
cout << 0 << endl;
return ;
}
else if (right_it == a.begin() + 1){
ans = min(ans, abs(value - *right_it));
}
else {
auto left_it = prev(right_it);
ans = min(ans, min(abs(value - *right_it), abs(value - *left_it)));
}
}
}
cout << ans << endl;
}
}
代码运用了大量指针操作,各种特判,难以阅读,debug。
指的一提的是,我一开始又把 lower_bound
记成找 val
大于等于的第一个数组中的元素,后面想起来是找数组中第一个大于等于 val
的元素。影响到了其中一个迭代器的特判,改完才过。
过是过了,代码仍然是依托史。
优化
-
先从最简单的复用性来看。
for (int i = 2; i <= n; i++) { ans = min(ans, a[i] - a[i - 1]); }
这段代码是找第一轮时的最小值,无论 \(k = 1\) 还是 \(k = 2\) 都是需要的,但我却在两个
if
中写了两遍。直接把代码块提到
if
外。 -
再从思路上优化。
for (int i = 1; i <= n; i++) { for (int j = i + 1; j <= n; j++) { ll value = a[j] - a[i]; auto right_it = lower_bound(a.begin() + 1, a.begin() + n + 1, value); if (right_it == a.begin() + n + 1) { ans = min(ans, abs(a[n] - value)); } if (*right_it == value) { cout << 0 << endl; return ; } else if (right_it == a.begin() + 1){ ans = min(ans, abs(value - *right_it)); } else { auto left_it = prev(right_it); ans = min(ans, min(abs(value - *right_it), abs(value - *left_it))); } } }
-
这一大坨特判,主要是我对于指针操作的ptsd。
- 比如第一个特判,我如果不加上,直接用第二个,如果第二个指针指向
end
,那就会出错。
- 比如第一个特判,我如果不加上,直接用第二个,如果第二个指针指向
-
一种行之有效的优化方式,不用指针。
-
把
lower_bound
返回的指针减去a.begin() + 1
,先转化为下标,之后都用下标角度考虑。这里注意,减去首指针之后,一定要加一,因为这个计算的是该元素到首指针的下标差,而我要求的是下标(我的下标从一开始)
int p = lower_bound(a.begin() + 1, a.begin() + n + 1, value) - (a.begin() + 1) + 1;
-
对于第一个
if
,因为哪怕 lb 找不到大于等于val
的元素,返回a.begin() + n + 1
减去之后也是得到 \(n\),正符合了我第一个特判用的 \(a_n - value\)。if (right_it == a.begin() + n + 1) { ans = min(ans, abs(a[n] - value)); }
这段代码等价于
ans = min(ans, abs(a[p] - value));
-
对于后面几个
if
无非是找与value
左右相邻两个的元素找最小的解,然后其中一个if
判断如果在最左边,就不能找最左边的。用指针的话,左边还要用到prev
,而下标直接简单相减就行。if (p <= n) ans = min(ans, a[p] - value); if (p >= 2) ans = min(ans, value - a[p - 1]);
-
优雅
void solve() { int n, k; cin >> n >> k; vector<ll> a(n + 1); for (int i = 1; i <= n; i++) cin >> a[i]; if (k >= 3) { cout << 0 << endl; return ; } sort(a.begin() + 1, a.begin() + n + 1); ll ans = a[1]; for (int i = 2; i <= n; i++) { ans = min(ans, a[i] - a[i - 1]); } if (k == 1) { cout << ans << endl; return ; } for (int i = 1; i <= n; i++) { for (int j = i + 1; j <= n; j++) { ll value = a[j] - a[i]; int p = lower_bound(a.begin() + 1, a.begin() + n + 1, value) - (a.begin() + 1) + 1; if (p <= n) ans = min(ans, abs(a[p] - value)); if (p >= 2) ans = min(ans, abs(value - a[p - 1])); } } cout << ans << endl; }
-