給定一個序列,序列上有若干\(mouse\)和若干\(hole\),求一組最優的\(mouse\)和\(hole\)的匹配。
定義一隻\(mouse\)跑到一個洞\(hole\)的代價爲兩點之間的距離。算法
每個洞不必定要進老鼠,但每個老鼠必定要進一個洞。spa
結論一:匹配不會交叉。(顯然)
結論二:對於一組匹配,老鼠和洞不會同時反悔。(從左往右進行匹配,同時反悔會產生交叉)code
而後就能夠用堆模擬費用流啦。
對老鼠和洞分別維護一個堆,設老鼠堆爲\(Q_1\),洞的堆爲\(Q_2\),表示增廣集合。
方便起見先往\(Q_2\)中放一個代價爲\(inf\)、容量爲\(inf\)的洞。io
代碼實現至關簡單:class
Q2.push((Item){inf , 1000000000}) ; Ans = 0 ; Item u ; ll t , w , fl , cnt ; for(int i = 1; i <= n; i ++) { if(!p[i].op) { u = Q2.top() ; Q2.pop() ; w = u.val + p[i].x ; Ans += w ; u.flow -- ; if(u.flow > 0) Q2.push(u) ; Q1.push((Item){- w - p[i].x , 1}) ; } else { cnt = 0 ; while(p[i].cap && !Q1.empty()) { u = Q1.top() ; w = p[i].x + u.val + p[i].cost ; if(w >= 0) break ; Q1.pop() ; fl = min(p[i].cap , u.flow) ; Ans = Ans + 1ll * w * fl ; u.flow -= fl ; p[i].cap -= fl ; cnt += fl ; if(u.flow > 0) Q1.push(u) ; Q2.push((Item){- w + p[i].cost - p[i].x , fl}) ; } if(p[i].cap > 0) Q2.push((Item){- p[i].x + p[i].cost , p[i].cap}) ; if(cnt > 0) Q1.push((Item){- p[i].cost - p[i].x , cnt}) ; } } cout << Ans << endl ; return 0 ;
堆模擬費用流,但複雜度沒有保證。
對老鼠和洞都進行兩種反悔(即老鼠和洞都使用問題二中洞的反悔方式)。
常見轉換有:若一個洞必須進老鼠、或者一隻老鼠必須進洞,那麼就把它的代價賦爲\(-inf\)。
部分問題的特殊性質能夠保證複雜度,其餘狀況下做爲一個暴力算法仍是很優秀的。im