★【動態規劃】【線段樹】基站選址

來源:互聯網
上載者:User
【問題描述】有N個村莊坐落在一條直線上,第i(i>1)個村莊距離第1個村莊的距離為Di。需要在這些村莊中建立不超過K個通訊基站,在第i個村莊建立基站的費用為Ci。如果在距離第i個村莊不超過Si的範圍內建立了一個通訊基站,那麼就成它被覆蓋了。如果第i個村莊沒有被覆蓋,則需要向他們補償,費用為Wi。現在的問題是,選擇基站的位置,使得總費用最小。【輸入格式】base.in輸入檔案的第一行包含兩個整數N,K,含義如上所述。第二行包含N-1個整數,分別表示D2,D3,…,DN ,這N-1個數是遞增的。第三行包含N個整數,表示C1,C2,…CN。第四行包含N個整數,表示S1,S2,…,SN。第五行包含N個整數,表示W1,W2,…,WN。【輸出格式】base.out輸出檔案中僅包含一個整數,表示最小的總費用。【輸入範例】3 21 22 3 21 1 010 20 30【輸出範例】4【資料規模】40%的資料中,N<=500;100%的資料中,K<=N,K<=100,N<=20,000,Di<=1000000000,Ci<=10000,Si<=1000000000,Wi<=10000。

此題考察用線段樹最佳化的動態規劃。

首先不難想到樸素方法:
狀態:f[k][i]表示將第k個基站建在第i個村莊的前i個村莊的最小費用。
f[k][i] = min(f[k - 1][p] + cost(p, i)) + c[i]。

這樣做的時間複雜度為O(n^3·k),顯然會逾時。

一種想法是,最佳化cost(p, i)的計算,使得減少枚舉量,但是這樣複雜度還是較高。

進一步思考,設村莊i的不被賠償範圍為[L[i], R[i]],那麼可以看出,隨著k和i的遞增枚舉(k在外層迴圈),若在某個位置i建立基站,在i左邊的村莊p需要被賠償,那麼在i之後的i',p也一定需要被賠償。

於是可以開個數組維護各個位置當前的費用,每次在其中找到最小的費用,再加上c[i]即可。

但是這樣時間效率仍然較低。

說起區間,不難想到線段樹,對!就是它了,可以把時間複雜度最佳化到(O(k·nlog n))。
把各個村莊按R從小到大的順序排序,每次枚舉i先將各個點的費用設為f[k - 1]中對應值,然後枚舉各個p(R[p] < d[i]),將所有的w[p]全部加到對應的區間[0, L[p])中,然後利用線段樹中維護的最小值來計算當前的f[k][i],即用這個最小值加上c[i]。

更新ans的值時,先繼續把枚舉i時還剩下的p按照上述規則依次加入到線段樹中,這時再用線段樹維護的最小值來更新ans。
Accode:

#include <cstdio>#include <cstdlib>#include <algorithm>#include <string>#define min(a, b) ((a) < (b) ? (a) : (b))const char fi[] = "base.in";const char fo[] = "base.out";const int maxN = 20010;const int MAX = 0x3f3f3f3f;const int MIN = ~MAX;struct SegTree {int L, R, lc, rc, Min, sum;};SegTree tr[maxN << 1];int f[maxN], L[maxN], R[maxN];int pre[maxN], ord[maxN];int d[maxN], c[maxN], s[maxN], w[maxN];int n, K, tot;void init_file(){    freopen(fi, "r", stdin);    freopen(fo, "w", stdout);    return;}inline int getint(){    int res = 0; char tmp;    while (!isdigit(tmp = getchar()));    do res = (res << 3) + (res << 1) + tmp - '0';    while (isdigit(tmp = getchar()));    return res;}inline bool cmpL(const int &a, const int &b){return L[a] < L[b];}inline bool cmpR(const int &a, const int &b){return R[a] < R[b];}void readdata(){    n = getint(); K = getint();    for (int i = 2; i <= n; ++i) d[i] = getint();    for (int i = 1; i <= n; ++i) c[i] = getint();    for (int i = 1; i <= n; ++i) s[i] = getint();    for (int i = 1; i <= n; ++i) w[i] = getint();    for (int i = 1; (ord[i] = i) <= n; ++i)        L[i] = d[i] - s[i], R[i] = d[i] + s[i];    std::sort(ord + 1, ord + n + 1, cmpL);    for (int i = 1, p = 1; i <= n; ++i)    while (p <= n && L[ord[p]] <= d[i])        pre[ord[p++]] = i;//用於記錄該點左邊第一個在其不賠償範圍之內的點。    std::sort(ord + 1, ord + n + 1, cmpR);    return;}inline void pushdown(int p){    tr[tr[p].lc].sum += tr[p].sum;    tr[tr[p].rc].sum += tr[p].sum;    tr[tr[p].lc].Min += tr[p].sum;    tr[tr[p].rc].Min += tr[p].sum;    tr[p].sum = 0;    return;}inline void update(int p){    tr[p].Min = min(tr[tr[p].lc].Min,                    tr[tr[p].rc].Min);    return;}void build(int L, int R){    int Now = ++tot;    tr[Now].L = L, tr[Now].R = R, tr[Now].sum = 0;    if (L == R) {tr[Now].Min = f[L]; return;}    int Mid = (L + R) >> 1;    tr[Now].lc = tot + 1; build(L, Mid);    tr[Now].rc = tot + 1; build(Mid + 1, R);    update(Now);    return;}void Add(int p, int L, int R, int delta){    if (L <= tr[p].L && R >= tr[p].R)    {        tr[p].sum += delta, tr[p].Min += delta;        return;    }    pushdown(p); //標記需要向下傳。    int Mid = (tr[p].L + tr[p].R) >> 1;    if (L <= Mid) Add(tr[p].lc, L, R, delta);    if (Mid < R) Add(tr[p].rc, L, R, delta);    update(p);    return;}void work(){    memset(f, 0x3f, sizeof f);    f[0] = 0;    int ans = MAX;    for (int k = 0, p, i; k < K + 1; ++k)    {        tot = 0; build(0, n);//為了方便計算,虛擬一個節點0出來。        for (i = p = 1; i < n + 1; ++i)        {            for (; p <= n && R[ord[p]] < d[i]; ++p)                Add(1, 0, pre[ord[p]] - 1, w[ord[p]]);            f[i] = tr[1].Min + c[i];        }        for (; p <= n; ++p)            Add(1, 0, pre[ord[p]] - 1, w[ord[p]]);        //要把i之後的費用全部加到其中,這樣才是真正的總費用。        ans = min(ans, tr[1].Min);    }     printf("%d\n", ans);    return;}int main() { init_file(); readdata(); work(); return 0;}#undef min

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.