1. 程式人生 > >BZOJ1150 [CTSC2007]數據備份Backup 【堆 + 鏈表】

BZOJ1150 [CTSC2007]數據備份Backup 【堆 + 鏈表】

esp 同時 getch .com 容易 圖片 最好的 AD 不同的

題目

  你在一家 IT 公司為大型寫字樓或辦公樓(offices)的計算機數據做備份。然而數據備份的工作是枯燥乏味
的,因此你想設計一個系統讓不同的辦公樓彼此之間互相備份,而你則坐在家中盡享計算機遊戲的樂趣。已知辦公
樓都位於同一條街上。你決定給這些辦公樓配對(兩個一組)。每一對辦公樓可以通過在這兩個建築物之間鋪設網
絡電纜使得它們可以互相備份。然而,網絡電纜的費用很高。當地電信公司僅能為你提供 K 條網絡電纜,這意味
著你僅能為 K 對辦公樓(或總計2K個辦公樓)安排備份。任一個辦公樓都屬於唯一的配對組(換句話說,這 2K
個辦公樓一定是相異的)。此外,電信公司需按網絡電纜的長度(公裏數)收費。因而,你需要選擇這 K 對辦公
樓使得電纜的總長度盡可能短。換句話說,你需要選擇這 K 對辦公樓,使得每一對辦公樓之間的距離之和(總距
離)盡可能小。下面給出一個示例,假定你有 5 個客戶,其辦公樓都在一條街上,如下圖所示。這 5 個辦公樓分
別位於距離大街起點 1km, 3km, 4km, 6km 和 12km 處。電信公司僅為你提供 K=2 條電纜。
技術分享圖片


  上例中最好的配對方案是將第 1 個和第 2 個辦公樓相連,第 3 個和第 4 個辦公樓相連。這樣可按要求使用
K=2 條電纜。第 1 條電纜的長度是 3km-1km=2km ,第 2 條電纜的長度是 6km-4km=2km。這種配對方案需要總長
4km 的網絡電纜,滿足距離之和最小的要求。

輸入格式

第一行包含整數n和k
其中n(2≤n≤100000)表示辦公樓的數目,k(1≤k≤n/2)表示可利用的網絡電纜的數目。
接下來的n行每行僅包含一個整數(0≤s≤1000000000),表示每個辦公樓到大街起點處的距離。
這些整數將按照從小到大的順序依次出現。

輸出格式

輸出應由一個正整數組成,給出將2K個相異的辦公樓連成k對所需的網絡電纜的最小總長度。

輸入樣例

5 2

1

3

4

6

12

輸出樣例

4

題解

很容易想到最後選擇的每對辦公樓一定是相鄰的,
因為最後\(ans = \sum Ri - Li\),R越左越好,L越右越好

這樣問題就轉化為了:有\(n-1\)個數,選取其中k個,使總和最小,所選數不能相鄰

直接貪心選最小是錯誤的,會發現樣例都過不了
因為如果選擇了最小的位置x,那麽\(x - 1\)\(x + 1\)兩個位置都不能選了
由貪心策略可知,x一定比\(x - 1\)\(x + 1\)要小,所以三個中只選一個一定是x最優
但如果三個中要選兩個,即選\(x - 1\)\(x + 1\),如果\(v[x - 1] + v[x + 1] - v[x]\)

,比剩余的數都要小,那麽顯然選擇兩個更優

這樣我們就得到一個算法:
建立一個堆,每次選出最小的數刪去並統計答案,同時刪去相鄰的兩個元素合並成一個新元素\(v[new] = v[x - 1] + v[x + 1] - v[x]\)加入堆中
對於三個數的討論是這樣,對於5個數的討論也類似,所以合並出來的元素被選擇時同樣也要與相鄰的元素合並
所以我們再用鏈表維護序列,寫一個支持刪除插入並記錄元素位置標號的堆,就可以A了

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long int
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define BUG(s,n) for (int i = 1; i <= (n); i++) cout<<s[i]<<‘ ‘; puts("");
#define ls (u << 1)
#define rs (u << 1 | 1)
#define fa (u >> 1)
using namespace std;
const int maxn = 100005,maxm = 200005,INF = 1000000000;
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57){if (c == ‘-‘) flag = -1; c = getchar();}
    while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
    return out * flag;
}
LL pre[maxm],val[maxm],post[maxm];
struct node{LL v,u;}H[maxm];
LL hsiz,pos[maxm];
LL n,k,A[maxn],ans;
int pd(int u){
    int small;
    while (true){
        small = u;
        if (ls <= hsiz && H[ls].v < H[small].v) small = ls;
        if (rs <= hsiz && H[rs].v < H[small].v) small = rs;
        if (small != u){
            pos[H[small].u] = u; pos[H[u].u] = small;
            swap(H[small],H[u]);
            u = small;
        }else break;
    }
    return u;
}
void pup(int u){
    while (u > 1 && H[fa].v > H[u].v){
        pos[H[fa].u] = u; pos[H[u].u] = fa;
        swap(H[fa],H[u]);
        u = fa;
    }
}
void ins(int u){
    H[++hsiz] = (node){val[u],u}; pos[u] = hsiz;
    pup(hsiz);
}
void del(int u){
    pos[H[hsiz].u] = u;
    swap(H[u],H[hsiz--]);
    u = pd(u);
    pup(u);
}
int main(){
    n = read(); k = read();
    REP(i,n) A[i] = read();
    sort(A + 1,A + 1 + n);
    for (int i = 1; i < n; i++){
        val[i] = A[i + 1] - A[i];
        if (i - 1) post[i - 1] = i,pre[i] = i - 1;
        ins(i);
    }
    node u;
    int x;
    while (k--){
        u = H[1]; x = u.u;
        ans += u.v;
        if (!pre[x]){
            del(1); del(pos[post[x]]);
            pre[post[post[x]]] = 0;
        }
        else if (!post[x]){
            del(1); del(pos[pre[x]]);
            post[pre[pre[x]]] = 0;
        }
        else {
            int l = pre[x],r = post[x];
            del(pos[l]); del(pos[r]);
            H[1].v = val[x] = val[l] + val[r] - val[x]; pos[x] = 1;
            pd(1);
            post[pre[x] = pre[l]] = x;
            pre[post[x] = post[r]] = x;
        }
    }
    cout << ans << endl;
    return 0;
}

BZOJ1150 [CTSC2007]數據備份Backup 【堆 + 鏈表】