图论:网络流最小费用最大流

P3381 【模板】最小费用最大流

添加了弧优化

点击查看折叠代码块
/*
最大流最小费用
费用为单位流量的费用
在最大流的前提下找最短路径即为最小费用
边权为当前边的流量乘以单位流量的费用

首先构建一个图用最短路算法来找到源点到各个点的最短距离
找到这个数据之后,我们就可以沿着最短路来进行增广,
即在最短路中求到一条可行路然后修改其残量,我们可以保证其为最大流中的一部分的最小花费
不断的进行增广直到我们找到了全部值,然后得解,
这就是将dinic和spfa结合起来的求解最小费用最大流问题的方法

spfa使用small label first 优化,用deque实现 

若要求最大费用
将单位费用改为-w,最终答案取相反数即为最大费用
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxm=1e6+10;
const int maxn=5e3+10;
const ll inf = (1ll<<62);
struct edge{
	int v,next;
	ll c,w;//边最大流量,单位流量的费用 
}e[maxm<<1];

int head[maxn],cnt=0;
int n,m,s,t;

void add(int u,int v,ll c,ll w){
    e[cnt].v=v;e[cnt].c=c;e[cnt].w=w;e[cnt].next=head[u];head[u]=cnt++;
    e[cnt].v=u;e[cnt].c=0;e[cnt].w=-w;e[cnt].next=head[v];head[v]=cnt++;
}

void init(){
	memset(head,-1,sizeof(head));
	cnt=0;
}
int tot;
ll mincost = 0,maxflow = 0;
int cur[maxn];//当前弧优化
ll dis[maxn];
bool vis[maxn];
bool spfa(int s,int t){
	for (int i=0;i<=tot;i++) dis[i] = inf,vis[i] = 0;//这里需要注意初始化应该要初始化到最大上界tot
	dis[s] = 0;
	vis[s] = 1;
	deque<int> dq;
	dq.push_front(s);
	
	while(!dq.empty()){
		int u=dq.front();
		dq.pop_front();
		vis[u] = 0;
		for (int i=head[u];~i;i=e[i].next){
			int v=e[i].v;
			ll c=e[i].c;//边的流量 
			ll w=e[i].w;//单位流量的花费——边的权值 
			if(c>0 && dis[v] > dis[u] + w){//当前有残量并且是最短路上的边 
				dis[v]=dis[u]+w;//更新最短路
				if(!vis[v]){
					vis[v]=1;
					if(!dq.empty() && dis[dq.front()]>dis[v]){//SLF优化,如果v的dis比队列的头dis较小,放入队首
						dq.push_front(v);
					}
					else dq.push_back(v);
				}
			}
		}
	}
	return dis[t]!=inf;
}

ll dfs(int u,int t,ll flow){//在最短路的基础上在残量网络中找增广路 
	if(u == t) return flow;
	ll delta = flow;
	vis[u] = true;
	for (int i=cur[u];~i;i=e[i].next){
		cur[u]=i;
		int v=e[i].v;
                ll w=e[i].w,c=e[i].c;
		if(c>0 && !vis[v] && dis[v] == dis[u] + w){//注意vis的初始化,spfa和dfs中都用到了 
			ll d=dfs(v,t,min(c,delta));
                        if(!d) dis[v] = 0;
			e[i].c-=d;
			e[i^1].c+=d;
			delta-=d;
			mincost+=w*d;
			if(delta == 0) break;
		}
	}
	vis[u] = false;
	return flow-delta;
}

void min_cost_max_flow(int s,int t){
	while(spfa(s,t)){
		memset(vis,0,sizeof(vis));
                memcpy(cur,head,sizeof(cur));
		maxflow+=dfs(s,t,inf);
	}
}

int main(){
    init();
    cin>>n>>m>>s>>t;
    tot = n;
    for (int i=1;i<=m;i++){
        int u,v;ll w,c;
        scanf("%d%d%lld%lld",&u,&v,&c,&w);//将单位费用改为-w,最终答案取相反数即为最大费用
        add(u,v,c,w);
    }
    min_cost_max_flow(s,t);
    printf("%lld %lld
",maxflow,mincost);
    return 0;
}
你将不再是道具,而是成为人如其名的人
原文地址:https://www.cnblogs.com/wsl-lld/p/13393592.html