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

https://www.luogu.com.cn/problem/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;
    }
    
posted @ 2024-01-21 19:08  加固文明幻景  阅读(12)  评论(0编辑  收藏  举报