To_Heart—总结——最小费用流 dijkstra+EK

前言

因为害怕 SPFA 哪天就死了,所以好好学习天天向上的学习 dijkstra 吧!

最小费用流 dijkstra+EK

因为费用有可能是负数,但我们 可爱 的 d i j k s t r a dijkstra dijkstra 并不能处理负的边权,所以我们需要利用 J o h n s o n Johnson Johnson 算法中的势函数:

我们对于每一个点定义一个势函数 h h h,把每条边权 w i w_i wi 改为 w i + h i − h j w_i+h_i-h_j wi+hihj,其中 h i h_i hi h j h_j hj 表示这条边的 起点 和 终点 的势函数。

而且我们考虑从起点 s s s 到 任意点 i i i 的一条路径,如果每条边都这样处理,实际上路径上除了 s s s i i i 之外的其他点的势函数都被抵消了,所以每一条边的相对大小是没有改变的,所以我们找的最短路的路径是不会改变的。且因为路径上的两点势函数抵消了,所以算出来的答案只需要在减去 h t − h s h_t-h_s hths 就行了。

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define int ll 
#define fir first
#define sec second
const ll INF=0x3f3f3f3f;
struct zz{
	int u,w,f,id;
};
vector<zz> v[200005];

struct EK{
	int s,t,MinCost,MaxFlow,len;
	int Dist[5005],h[5005],f[5005];
	int pre[5005],e[5005];
	void Add(int x,int y,int z,int val){
		int idx=v[x].size(),idy=v[y].size();
		v[x].push_back((zz){y,z,val,idy});
		v[y].push_back((zz){x,0,-val,idx});
	} 
	bool Dijkstra(){
		memset(f,0,sizeof f);memset(Dist,0x3f,sizeof Dist);	
		priority_queue<pair<int,int> > q; q.push({0,s});Dist[s]=0;
		while(q.size()){
			int x=q.top().sec;q.pop(); if(f[x]) continue; f[x]=1;
			for(int i=v[x].size()-1;~i;i--){
				int y=v[x][i].u,w=v[x][i].w,val=v[x][i].f;
				if(w&&Dist[y]>Dist[x]+val+h[x]-h[y]){
					Dist[y]=Dist[x]+val+h[x]-h[y];
					pre[y]=x,e[y]=i;
					if(!f[y]) q.push({-Dist[y],y});
				}
			}
		}
		for(int i=0;i<=len;i++) if(Dist[i]<INF) h[i]+=Dist[i];
		memset(f,0,sizeof f);
		return Dist[t]<INF;
	}
	int DFS(int x,int sum){
		if(x==t||!sum) return sum;
		int siz=v[x].size(),ans=0;f[x]=1;
		for(int i=0;i<siz&&sum!=ans;i++){
			int y=v[x][i].u,w=v[x][i].w,id=v[x][i].id,val=v[x][i].f;
			if(!w||f[y]||h[x]+val!=h[y]) continue;
			int now=DFS(y,min(sum-ans,w));
			v[x][i].w-=now,v[y][id].w+=now,ans+=now,MinCost+=now*val;
		}
		f[x]=0;
		return ans;
	}
	void dinic(){
		int now=0;
		while(Dijkstra()) while(now=DFS(s,INF)) MaxFlow+=now;
	}
	void Ek(){
		int T=0;
		while(Dijkstra()){
			int Min=INF;
			for(int y=t,x=pre[t];y!=s;y=pre[y],x=pre[y]) Min=min(Min,v[x][e[y]].w);
			for(int y=t,x=pre[t];y!=s;y=pre[y],x=pre[y])
				v[x][e[y]].w-=Min,v[y][v[x][e[y]].id].w+=Min;
			MinCost+=h[t]*Min,MaxFlow+=Min;
		}
	}
}T; 

int n,m; 

signed main(){
	cin>>n>>m>>T.s>>T.t;
//	T.s=1,T.t=n;
	T.len=n;
	for(int i=1,x,y,z,val;i<=m;i++) scanf("%lld%lld%lld%lld",&x,&y,&z,&val),T.Add(x,y,z,val);
	T.dinic();
	printf("%lld %lld\n",T.MaxFlow,T.MinCost);
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值