图论:网络流最小费用最大流
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\n",maxflow,mincost);
return 0;
}