題解/算法 {2279. 网络战争}
@LINK: https://www.acwing.com/problem/content/description/2281/
;
01分數規劃 求最小值, 定義二分函數為... <= mid
, (Ei - mid)的和 <= 0
, 因此我們得到一個新的無向圖G0
他每條邊的權值 等於原圖中邊權-= mid
;
問題轉換為: 在G0中, 是否可以找到點S,T
的一個(邊權之和<=0
的)邊割集; 再轉換為: 找到邊割集的邊權和的最小值;
通過最大流等於最小割定理 可以解決這個問題, 因爲最大流所得到的最小割(即一個邊割集) 這個邊割集 可以對應到無向圖的情況, 需要做以下轉換:
0: 把負權邊給處理掉(因爲流網絡不允許有負權邊), 所有的<=0
的邊 一定是可以放到最小的邊割集, 因此 令ANS(邊割集的最小和): 所有負權邊的和
, 然後把這條邊給去掉 也就是將他的邊權(容量)置爲0
; 令此時這個無向圖為G0
;
1: 將無向圖G0
轉換成有向圖G1
(因爲流網絡必須是有向圖), 一個無向邊 對應2條有向邊;
2: 此時這個有向圖G1
求最大流(他雖然是這個有向圖的最小邊割集的值 但其實他也是G0
的最小邊割集;
.
首先, 最大流等於最小割(割邊都是滿流的) 因此我們求出了G1
的最小的邊割集; 其次 我們要證明 G1
的最小割等於G0
的最小割 參見@LINK: (https://editor.csdn.net/md/?not_checkout=1&articleId=136381246)-(@LOC_0)
;
.
ANS += 最大流
, 現在ANS
就是答案(最小的邊割集), 判斷ANS <= 0
即可;
無向邊a-b
對應a<->b
兩條有向邊, 其實你的流網絡 就可以直接這樣建立 (是可以這樣做的), 但有點不符合常規流網絡的規定 因爲現在奇數編號的邊 就不再是反向邊了, 我們還是按照常規操作 繼續把a<->b
對應到4條邊上;
void ___Solve( [[maybe_unused]] int ___testID){
int N, M, S, T; cin>>N>>M>>S>>T; --S,--T;
vector<pair<int,int> > Edges;
static ___MaxFlow< Double> MaxFlow;
MaxFlow.Initialize( N, M*2);
FOR_( m, 1, M){
int a, b, w; cin>>a>>b>>w; --a,--b;
MaxFlow.Add_edge( a, b, 0); MaxFlow.Add_edge( b, a, 0);
//> 記錄兩條*正向邊*的ID;
Edges.emplace_back( MaxFlow.G_res.EdgesCount - 2, w); Edges.emplace_back( MaxFlow.G_res.EdgesCount - 4, w);
}
{ // 浮点数二分
auto __check_success = [&]( Double _mid)->bool{ // 檢查最小割是否`<=mid`;
Double minCut = 0;
MaxFlow.Clear_flow();
for( auto & e : Edges){
auto & edge = MaxFlow.G_res.Weight[ e.first];
auto w = Double(e.second) - _mid;
if( w <= 0){ minCut += w; edge = 0;}
else{ edge = w;}
}
minCut /= 2; // 注意這裏!!! 因爲一個無向邊對應了2條有向邊 上面會計算2次 所以要`/=2`;
minCut += MaxFlow.Work( S, T);
return minCut <= 0;
};