模擬費用流的基本模型

2022-04-02 22:25:35 字數 4537 閱讀 4059

在本蒟蒻學習的過程中參考了這位和這位dalao的部落格

費用流,是oi中解決最優化最優化問題的乙個常用演算法。但眾所周知費用流的模型雖然很容易構建,但他的時間效率卻比較低下

模擬費用流方法是指利用除費用流以外的手段解決一些費用流問題。一般來說,乙個問題如果使用模擬費用流演算法來解決,你在整個**中不會見到任何乙個與費用流有關的片段。可以說,這個方法非常的抽象

而在這乙個演算法的學習過程中,也存在著一些比較實用的模型。通過對這些模型的分析讓我們可以對模擬費用流的理解步步加深

你現在正在處理乙個問題:

在一條數軸上有一些兔子和一些洞,其中兔子的座標為\(x_i\),洞的座標為\(y_i\)。兔子只能向左走,現在你要最小化所有兔子行走的距離之和。

對於這個問題,很顯然我們按座標排序後從左到右操作,每次遇到兔子的時候考慮找到它前面的最近的未匹配的洞

用乙個就可以維護上面的操作

在之前的基礎上,洞有了附加權值\(v_i\),這意味著如果要選擇這個洞那麼必須多付出\(v_i\)的代價(相當於開啟洞門?)。還是求最小的代價之和。

還是和之前一樣,從左往右考慮每個兔子和洞

如果當前這個位置是乙個兔子\(a\),那麼讓它進入之前的乙個洞\(b\)的代價就是\(x_a-y_b+v_b\),這也就意味著我們要取\(-y_b+v_b\)最小的洞,可以用乙個來儲存所有的洞

但我們發現這個貪心的想法只侷限於眼下,可能最優的解是另乙個兔子\(c\)來和洞\(b\)匹配,然後\(a\)不匹配。那麼我們考慮去掉\(a\)對答案的影響,那麼就是要減去它的影響\(x_a\)

那麼怎麼處理呢,很簡單,我們選擇了\(a\)之後就向堆裡扔進去乙個權值為\(-x_a\)的洞。這樣選擇了這個洞就意味這進行了反悔操作

注意這裡我們取消\(a,b\)的匹配,改為\(c,b\)的匹配,這個操作是不是很像費用流的退流操作,因此實際上模擬費用流的本質就是用其他的東西來模擬退流,也就是說我們要考慮怎麼在貪心的基礎上實現反悔

好了知道了這個模型之後你就可以去做一道板題了:bzoj 4977 跳傘求生,就是把這裡的最小值改成最大值,維護的方法完全類似

#include#include#include#define ri register int

#define ci const int&

using namespace std;

const int n=100005;

struct data

else hp.push(a[i].val-a[i].pos);

return printf("%lld",ans),0;

}

在之前(或之後)的任意一種模型中,每個兔子必須找到洞來匹配。

考慮在開始時就讓每乙個兔子都與乙個距離它\(\infty\)的洞匹配,這樣這個匹配就無法退流

兔子洞有額外權值\(s_i\)。兔子可以向左右兩邊走。

注意,這是所有模型中可以說是最重要的乙個了。下面的模型都和它密切相關,一定要掌握透徹

還是和上面的分析類似,我們仍舊是從左向右考慮這個過程,並且還要考慮兔子和後面的洞匹配所帶來的一系列情況

設\(v_i\)表示當兔子找到洞\(i\)時需要付出的額外代價(可以看做維護洞的堆的堆頂),同理設\(w_i\)表示洞選擇兔子\(i\)付出的額外代價

然後,我們發現尤其是在第二種情況中,這種\(c\)搶走了\(b\),\(a\)就去匹配它本來匹配過的\(d\),然後現在匹配\(d\)的點又去……的操作,是不是就是費用流的推流操作(前面的只退一次,這裡就更複雜了)

這時,兔子已經不在是兔子,洞也已經不再是洞了。洞和兔子因為彼此的利益關係交織在一起,但是無論如何,我們只需要乙個堆就可以把它們處理地服服帖帖的

因此,關鍵的關鍵還是通過新增了一些「洞」 和 「兔子」 完成了兩者的 「反悔」 操作

我們在以上的任意模型中,兔子和洞都有分身(即乙個位置上會有很多個兔子和洞),記為\(c_i,d_i\)。求最小的代價和

我們可以把分身看做有許多個單獨的情況。但是當分身的數目很多(到\(10^9\)級別)的時候就不能這麼搞了

考慮直接把相同的兔子和洞合併在一起,用乙個pair綁起來然後用上面的方法做即可

什麼?你說合併的時候產生的新兔子和新洞的數目可能會很多?這個貌似可以用匹配不交叉來證,但是我太弱了不會。不過你只要知道新新增的點都是常數級別的就好了

那麼接下來就可以去做一道題了:uoj#455 雪災與外賣

#include#include#include#include#define ri register int

#define ci const int&

using namespace std;

typedef long long ll;

const int n=100005;

const ll inf=1e12;

struct event

}; priority_queue a,b; long long sum,ans; //a-holes b-rabbits

int main()

if (cs) b.push(data(-a[i].pos-a[i].val,cs));

if (left) a.push(data(a[i].val-a[i].pos,left));

} return printf("%lld",ans),0;

}

在模型四的基礎上,分身必須匹配至少乙個

我們把分身拆成兩種,一種有乙個,匹配了可以產生額外價值\(-\infty\),另一種有\(c_i-1\)個,匹配後產生額外價值\(0\)

把兔子和兔子洞搬到樹上。兩點間距離定義為樹上距離。

還是考慮先定下一種方向,那麼我們從底向上匹配,每次合併乙個點不同子樹的東西,把堆改為可並堆即可

然後又是一道題:loj#6405 征服世界

#include#include#include#define ri register int

#define ci const int&

#define cl const ll&

#define mp make_pair

#define fi first

#define se second

using namespace std;

typedef long long ll;

typedef pair pi;

const int n=250005;

const ll inf=1e12;

struct edge

e[n<<1]; int n,head[n],u,v,c,x[n],y[n],cnt; ll ans,dis[n];

class lefty_tree

node[n*20]; int rt[n*20],tot;

#define lc(x) node[x].ch[0]

#define rc(x) node[x].ch[1]

#define d(x) node[x].dis

#define v(x) node[x].v

inline int merge(int x,int y)

public:

inline void insert(ci pos,cl val,ci num)

inline bool non_empty(ci x)

inline void union(ci x,ci y)

inline pi top(ci x)

inline void remove(ci x)

inline void updata(ci x,ci y)

#undef lc

#undef rc

#undef d

#undef v

}x,y; int totx,toty;

inline void addedge(ci x,ci y,ci z)

; head[x]=cnt;

e[++cnt]=(edge); head[y]=cnt;

}inline void union(ci x,ci y,cl d)

}#define to e[i].to

inline void dfs(ci now=1,ci fa=0)

}#undef to

int main()

{ //freopen("code.in","r",stdin); freopen("code.out","w",stdout);

ri i; for (scanf("%d",&n),i=1;i這就完了?其實才剛剛開始呢……

NOI2019 序列(模擬費用流)

有兩個長度為n的序列,要求從每個序列中選k個,並且滿足至少有l個位置都被選,問總和最大是多少。1 leq l leq k leq n leq 2 10 5 首先,記錄當前考慮到的位置i,第乙個選的數量a,第二個選的數量b,都被選的數量c,可以做到 o n 4 卡常後能過 n leq 150 有40分...

貪心(模擬費用流) NOIP2011 觀光公交

風景迷人的小城y 市,擁有n 個美麗的景點。由於慕名而來的遊客越來越多,y 市特意安排了一輛觀光公交車,為遊客提供更便捷的交通服務。觀光公交車在第0 分鐘出現在1號景點,隨後依次前往2 3 4 n 號景點。從第i 號景點開到第i 1 號景點需要di 分鐘。任意時刻,公交車只能往前開,或在景點處等待。...

NOI2019 序列(模擬費用流)

有兩個長度為n的序列,要求從每個序列中選k個,並且滿足至少有l個位置都被選,問總和最大是多少。1 leq l leq k leq n leq 2 10 5 首先,記錄當前考慮到的位置i,第乙個選的數量a,第二個選的數量b,都被選的數量c,可以做到 o n 4 卡常後能過 n leq 150 有40分...