資料備份(小根堆 貪心 思維)

2021-10-10 16:08:44 字數 2995 閱讀 1296

總共 n

nn 棟樓,要分配 k

kk 條電纜給 k

kk 對樓,每棟樓只能被配對一次。要使每對樓之間的距離總和最小。

容易想到,只有相鄰的兩棟樓來配對,最終得到的距離總和才會最小。

所以,我們可以將每相鄰的兩棟樓之間的距離預處理出來,再進行配對。

用乙個陣列 d

dd 來存每一段距離,d[i

]d[i]

d[i]

表示第 i

ii 棟樓與第 i−1

i-1i−

1 棟樓之間的距離。

我們可以用乙個小根堆來維護所有的 d

dd,每次都選出最小的值,然後累加到答案中。但是,我們發現每棟樓只能被配對一次,如果單純地這樣維護,可能會有樓被重複配對。

這裡有乙個很強的性質:

若 d

id_i

di​ 是最小值,則它可以被選擇,且 di−

1d_

di−1

​ 和 di+

1d_

di+1

​ 不能選。

若不選 d

id_i

di​,則答案貢獻中必須選擇 di−

1d_

di−1

​ 和 di+

1d_

di+1​。

證明如下:

假設最小值 d

id_i

di​ 被選擇,則稱它是被孤立選擇,若是選擇 di−

1d_

di−1

​ 和 di+

1d_

di+1

​,則稱這一對數非孤立。容易知道,每乙個值只有孤立被選和非孤立被選兩種情況。

第一種情況:若選擇孤立值 d

id_i

di​,則最終的選擇方案中,必須從 [1,

i−2]

[1,i-2]

[1,i−2

] 或 [i+

2,n]

[i+2,n]

[i+2,n

] 中選擇 k−1

k-1k−

1 個孤立或兩個配對的非孤立節點。

第二種情況:若選擇非孤立節點 di−

1d_

di−1

​ 時不選擇其配對的非孤立節點 di+

1d_

di+1

​,則表示我們可以從其他地方找到乙個孤立節點(其他區域的最小值)d

jd_j

dj​,此時我們選了 di−

1,dj

d_,d_j

di−1​,

dj​,但是由於 di≤

di−1

,di+

1d_i\leq d_,d_

di​≤di

−1​,

di+1

​,所以在最優的情況中,選了 d

jd_j

dj​ 一定可以不選 di−

1d_

di−1

​ 而選 d

id_i

di​,這就發生了矛盾。所以如果選擇了非孤立節點 di−

1d_

di−1

​ 則必須也選擇與之配對的非孤立節點 di+

1d_

di+1​。

於是,我們可以用小根堆來維護每乙個距離節點 d

id_i

di​,並且將其分成兩種情況,一種是孤立取出,一種是取出非孤立的 di−

1,di

+1

d_,d_

di−1​,

di+1

​。但是由於不知道要取出孤立節點還是取出非孤立的節點,我們可以用乙個雙端鍊錶來維護 d

dd 陣列,如果取出了 d

id_i

di​ 就將 di−

1,di

+1

d_,d_

di−1​,

di+1

​ 同時刪去,並且讓 di=

di−1

+di+

1−di

d_i=d_+d_-d_i

di​=di

−1​+

di+1

​−di

​,並將其重新推入小根堆中進行維護(此時相當於把第二個情況也加進小根堆)。

#include

#include

#include

#include

#define sc scanf

#define pf printf

using

namespace std;

typedef

long

long ll;

typedef pairint> pli;

const

int n =

1e5+10;

ll l[n]

, r[n]

, d[n]

;int n, k;

bool vis[n]

;void

delete_node

(int p)

intmain()

);}

ll ans =0;

while

(k--

) ll v = it.first;

int p = it.second, left = l[p]

, right = r[p]

; ans +

= v;

d[p]

= d[left]

+ d[right]

- d[p]

; heap.

push()

; vis[left]

= vis[right]

=true

;delete_node

(left)

,delete_node

(right);}

pf("%lld\n"

, ans)

;return0;

}

堆(大根堆 小根堆)

堆又可稱之為完全二叉堆。這是乙個邏輯上基於完全二叉樹 物理上一般基於線性資料結構 如陣列 向量 鍊錶等 的一種資料結構。學習過完全二叉樹的同學們都應該了解,完全二叉樹在物理上可以用線性資料結構進行表示 或者儲存 例如陣列int a 5 就可以用來描述乙個擁有5個結點的完全二叉樹。那麼基於完全二叉樹的...

堆(Heap)大根堆 小根堆

具有以下的特點 1 完全二叉樹 2 heap中儲存的值是偏序 min heap 父節點的值小於或等於子節點的值 max heap 父節點的值大於或等於子節點的值 一般都用陣列來表示堆,i結點的父結點下標就為 i 1 2。它的左右子結點下標分別為2 i 1和2 i 2。如第0個結點左右子結點下標分別為...

堆(Heap)大根堆 小根堆

目錄一般都用陣列來表示堆,i結點的父結點下標就為 i 1 2。它的左右子結點下標分別為2 i 1和2 i 2。如第0個結點左右子結點下標分別為1和2。插入乙個元素 新元素被加入到heap的末尾,然後更新樹以恢復堆的次序。每次插入都是將新資料放在陣列最後。可以發現從這個新資料的父結點到根結點必然為乙個...