【dijkstra求MCFC】

紫荆峰

关注

阅读 37

2022-01-06

使用势能函数解决问题。
因为边权为负时无法使用 dijkstra 求最短路,所以我们要使最短路不被改变的情况下使边权变为正。
不能直接加边,因为路径的边数不同。
可以证明当 h[i]=dis[i] 时 dis’[t]=dis[t]-h[t]。
当有反向边加入时依然成立。
那么先用一次 SPFA 求出原始 dis 即可。
code:

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
#define int long long
#define db double
const int MAXN=5e3+5;
const int MAXM=2e5+5;
const int INF=0x3f3f3f3f;
int n,m,ca[MAXM],cnt=1,head[MAXN],dis[MAXN],pre[MAXN],mf,mc,inc[MAXN],st=0,en,h[MAXN],s,t;
db Ans;
bool vis[MAXN];
struct EDG{
	int nxt,to,z;
}a[MAXM];
struct ren{
	int v,val;
	ren(){};
	ren(int V,int Val){
		v=V;val=Val;
	}
	bool operator<(const ren &a)const{
		return val>a.val;
	}
};
priority_queue<ren> p;
void add(int x,int y,int c,int w)
{
	a[++cnt].to=y;
	a[cnt].nxt=head[x];
	a[cnt].z=w;
	head[x]=cnt;
	ca[cnt]=c;
}
void spfa(int s,int t)
{
	memset(dis,0x3f,sizeof(dis));
	memset(vis,0,sizeof(vis));
	inc[s]=INF;
	vis[s]=1;dis[s]=0;
	queue<int> q;
	q.push(s);
	while(q.size()){
		int now=q.front();
		q.pop();
		vis[now]=0;
		for(int i=head[now];i;i=a[i].nxt){
			if(!ca[i]) continue;
			int v=a[i].to;
			if(dis[v]>dis[now]+a[i].z)
			{
				dis[v]=dis[now]+a[i].z;
				pre[v]=i;
				inc[v]=min(inc[now],ca[i]);
				if(!vis[v]) vis[v]=1,q.push(v);
			}
		}
	}
}
bool dij(int s,int t)
{
	memset(dis,0x3f,sizeof(dis));
	memset(vis,0,sizeof(vis));
	inc[s]=INF;
	dis[s]=0;
	p.push((ren){s,0});
	while(p.size()){
		ren now=p.top();
		p.pop();
		if(vis[now.v]) continue;
		vis[now.v]=1;
		for(int i=head[now.v];i;i=a[i].nxt){
			int v=a[i].to;
			if(!ca[i]) continue;
			if(dis[v]>dis[now.v]+a[i].z+h[now.v]-h[v]){
				dis[v]=dis[now.v]+a[i].z+h[now.v]-h[v];
				pre[v]=i;
				inc[v]=min(inc[now.v],ca[i]);
				p.push((ren){v,dis[v]});
			}
		}
	}
	return dis[t]<INF;
}
void MCMF(int s,int t){
	mc=0;mf=0;
	while(dij(s,t)){
		int x=inc[t];
		mf+=x;
		mc+=(dis[t]+h[t])*x;
		int op=t,i;
		while(op!=s){
			i=pre[op];
			ca[i]-=x;
			ca[i^1]+=x;
			op=a[i^1].to;
		}
		for(int i=1;i<=n;i++){
			h[i]+=dis[i];
		}
	}
}
signed main(){
	scanf("%lld%lld%lld%lld",&n,&m,&s,&t);
	for(int i=1;i<=m;i++){
		int x,y,c,z;
		scanf("%lld%lld%lld%lld",&x,&y,&c,&z);
		add(x,y,c,z);add(y,x,0,-z);
	}
	spfa(s,t);
	for(int i=1;i<=n;i++){
		h[i]=dis[i];
	}
	MCMF(s,t); 
	printf("%lld %lld\n",mf,mc);
	return 0;
}

精彩评论(0)

0 0 举报