總共 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的末尾,然後更新樹以恢復堆的次序。每次插入都是將新資料放在陣列最後。可以發現從這個新資料的父結點到根結點必然為乙個...