对CF1904C的代码优化(复用性、指针转下标)

https://www.luogu.com.cn/problem/CF1904C

分讨,然后 k=2 的时候肯定要写暴力,但是我的暴力很不优雅。

1|0石山

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 的元素。影响到了其中一个迭代器的特判,改完才过。

过是过了,代码仍然是依托史。

2|0优化

  • 先从最简单的复用性来看。

    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,正符合了我第一个特判用的 anvalue

        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]);

    3|0优雅

    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; }

__EOF__

本文作者Kdlyh
本文链接https://www.cnblogs.com/kdlyh/p/17978185.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   加固文明幻景  阅读(14)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下
点击右上角即可分享
微信分享提示