1. 程式人生 > 其它 >[模板] 最小費用最大流

[模板] 最小費用最大流

一、題目

點此看題

二、解法

講一種勢能 \(\tt dijkstra\) 的做法(簡稱勢能演算法),因為 \(\tt spfa\) 在單次擴充套件的時候可能會被卡到 \(O(nm)\),而勢能 \(\tt dijkstra\) 的時間複雜度是嚴格的 \(O(m\log n)\),在一些擴充套件次數較小的毒瘤題中可能會用到。

勢能演算法的中心思路是化負權邊為非負權邊,我們設計勢能函式 \(h(x)\) 來調整邊權,這裡的勢能和物理中的定義是很相似的,也就是勢能變化之和初末位置有關,而和路徑無關。

\(w'(u,v)=w(u,v)+h(u)-h(v)\),不難發現最後跑出來的最短路要加上 \(h(t)-h(s)\)

關鍵是如何使得 \(w(u,v)+h(u)-h(v)\geq0\) 恆成立,設 \(d(u)\)\(s\)\(u\) 真正的最短路,設 \(dis(u)\)\(s\)\(u\) 通過轉化後的邊求出來的最短路,令 \(h(s)=0\),有 \(dis(u)+h(u)=d(u)\)

如果我們再每次做完最短路之後讓 \(h(u)\leftarrow d(u)\) 是滿足條件的,因為如果是原來的邊那麼顯然 \(d(u)-d(v)+w(u,v)\geq 0\),如果是新產生的取負的邊那麼 \(d(u)=d(v)+w,(w>0)\rightarrow d(u)-d(v)-w=0\),新的邊權就是 \(0\)

,也滿足條件。那麼每次我們把 \(h(u)\) 加上 \(dis(u)\) 就可以維護勢能函數了。

時間複雜度 \(O(flow\cdot m\log n)\)

#include <cstdio>
#include <queue>
using namespace std;
const int M = 50005;
const int inf = 0x3f3f3f3f;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,s,t,tot,f[M],dis[M],fw[M],pre[M],lst[M],h[M];
struct edge
{
	int v,f,c,next;
}e[2*M];
struct node
{
	int u,c;
	bool operator < (const node &b) const
	{
		return c>b.c;
	}
};
void add(int u,int v,int F,int c)
{
	e[++tot]=edge{v,F,c,f[u]},f[u]=tot;
	e[++tot]=edge{u,0,-c,f[v]},f[v]=tot;
}
int bfs()
{
	for(int i=1;i<=n;i++) dis[i]=inf;
	fw[s]=inf;pre[s]=lst[s]=dis[s]=0;
	priority_queue<node> q;
	q.push(node{s,0});
	while(!q.empty())
	{
		node t=q.top();q.pop();int u=t.u;
		for(int i=f[u];i;i=e[i].next)
		{
			int v=e[i].v,c=h[u]-h[v]+e[i].c;
			if(dis[v]>dis[u]+c && e[i].f)
			{
				dis[v]=dis[u]+c;
				pre[v]=u;lst[v]=i;
				fw[v]=min(fw[u],e[i].f);
				q.push(node{v,dis[v]});
			}
		}
	}
	return dis[t]<inf;
}
signed main()
{
	n=read();m=read();s=read();t=read();tot=1;
	for(int i=1;i<=m;i++)
	{
		int u=read(),v=read(),f=read(),c=read();
		add(u,v,f,c);
	}
	int ans=0,flow=0;
	while(bfs())
	{
		int zy=t;
		flow+=fw[t];
		ans+=(dis[t]+h[t])*fw[t];
		while(zy!=s)
		{
			e[lst[zy]].f-=fw[t];
			e[lst[zy]^1].f+=fw[t];
			zy=pre[zy];
		}
		for(int i=1;i<=n;i++)
			h[i]+=dis[i];
	}
	printf("%d %d\n",flow,ans);
}