1. 程式人生 > >BZOJ1237: [SCOI2008]配對

BZOJ1237: [SCOI2008]配對

esp 兩個 name png 需要 端點 情況 include col

感覺此題還是挺可做的...

首先考慮最無腦的做法:

  我需要知道接下來要給誰配對,在另一個數組中還沒有被選的有哪些,
  並記錄已選的造成的貢獻

要想知道沒選的有哪些的話,這直接記錄問題就很大了,考慮能不能省去這一步

那麽無非是兩個數組被選的集合的右端點同時往右擴張,
或是每個數可與其配對的數的範圍大概為一個常數

直覺是這樣的,考慮證明一下(其實我也不會證...)

先把兩個數組排序,
這樣對於一個 ai 來說他可選的一定是在 b 數組中的一段連續區間

在註意到每個數組中元素不重復之後,可以發現

顯然特別極端的情況是不能可能的,比如

技術分享圖片

這是顯然可以隨意調整一下的,最後最左邊的點一定是選擇一個離他較近的點的

想證明其他的基本同理,試圖枚舉下面的點與上面的值是否相同即可

然後大概感覺上就沒什麽問題了,四個的話也是可以枚舉值是否相同做的

好像就到 3 了,可以考慮 n = 3 且 a_i b_i 都相同的時候,
顯然是三個交叉選要優的,要麽就無解了。。

然後就粗糙的證完了,估計考場上也就這樣了


代碼:

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<cstdio>
#include<cmath>
using namespace std;

typedef long long ll;
const int MAXN = 100005;

int n;
ll a[MAXN], b[MAXN], f[MAXN];

int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) {
        scanf("%lld%lld", &a[i], &b[i]);
        f[i] = 200000000000000ll;
    }
    if(n == 1 && a[1] == b[1]) {
        puts("-1");
        return 0;
    }
    sort(a + 1, a + n + 1);
    sort(b + 1, b + n + 1);
    f[0] = 0ll;
    for(int i = 1; i <= n; ++i) {
        if(a[i] != b[i]) f[i] = f[i - 1] + abs(a[i] - b[i]);
        if(i >= 2 && a[i] != b[i - 1] && a[i - 1] != b[i]) f[i] = min(f[i], f[i - 2] + abs(a[i] - b[i - 1]) + abs(a[i - 1] - b[i]));
        if(i >= 3) {
            if((a[i] != b[i - 1]) && (a[i - 1] != b[i - 2]) && (a[i - 2] != b[i])) f[i] = min(f[i], f[i - 3] + abs(a[i] - b[i - 1]) + abs(a[i - 1] - b[i - 2]) + abs(a[i - 2] - b[i]));
            if((a[i] != b[i - 2]) && (a[i - 1] != b[i]) && (a[i - 2] != b[i - 1])) f[i] = min(f[i], f[i - 3] + abs(a[i] - b[i - 2]) + abs(a[i - 1] - b[i]) + abs(a[i - 2] - b[i - 1]));
        }
    }
    printf("%lld\n", (f[n] == 200000000000000ll ? -1 : f[n]));
    return 0;
}

  

BZOJ1237: [SCOI2008]配對