最小费用最大流学习笔记

最近学了最小费用最大流,写一篇总结

前置知识:

  • 最小费用的前提条件是最大流(本文提到的最大流算法为Dinic)
  • 对于每条边来说,他们有着不一定相等的单位流量的价格,所以才有了"最小费用"这一条件

个人理解:

  • 在普通的网络最大流中,常常使用类似while(bfs())while(flow=dinic(s,INF))maxflow+=flow;的方式求得结果
  • 而在最小费用流中,原本的bfs()被替换成了spfa().而原本的bfs()是用来进行分层的,也就是保证始终有一条增广路.如果无法获取新的增广路了,也就可以输出结果了.在最小费用流中,我们在进行bfs的同时增加了对最小费用的剪枝,也就是spfa().

模板地址:https://www.luogu.org/problemnew/show/P3381


#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
using namespace std;
const int MAXN=100100;
int head[MAXN],cnt=1;
int dis[MAXN],pre[MAXN],last[MAXN],flow[MAXN];
bool vis[MAXN];
struct Edge{
    int to,w,f,nxt;
}e[MAXN];
queue<int>q;
void addEdge(int u,int v,int w,int f){//加边 
    e[++cnt].to=v,e[cnt].w=w,e[cnt].f=f,e[cnt].nxt=head[u],head[u]=cnt;
    e[++cnt].to=u,e[cnt].w=0,e[cnt].f=-f,e[cnt].nxt=head[v],head[v]=cnt;
}
bool spfa(int s,int t){
    memset(dis,0x7f,sizeof(dis));//清空数组 
    memset(flow,0x7f,sizeof(flow));
	pre[t]=0;//将汇点的前继点设为空值 
    q.push(s);//初始化源点 
    dis[s]=0;//初始化dis 
    while(q.size()){//如果队列中还有结点就继续bfs 
        int u=q.front();q.pop();vis[u]=0;//提出点并设置该点并没有访问过 
        for(int i=head[u];i;i=e[i].nxt){
        	int y=e[i].to;
        	int newdis=dis[u]+e[i].f;//到点y的新代价 
            if(e[i].w&&dis[y]>newdis){//如果还有容量并且新路径代价较小 
                dis[y]=newdis;//设置到点y的代价为新代价 
                pre[y]=u;//到点y需要经过的点 
                last[y]=i;//到点y需要经过的边 
                flow[y]=min(flow[u],e[i].w);//能经过该点的最大流 
                if(!vis[y]){//如果该点没访问过
                    vis[y]=1;//将该点设为访问过 
                    q.push(y);//向队列中加点 
                }
            }
        }
    }
    return pre[t];
}
int main(){
    int n,m,s,t;
    scanf("%d%d%d%d",&n,&m,&s,&t);
    for(int i=1;i<=m;i++){//输入
    	int u,v,w,f;
        scanf("%d%d%d%d",&u,&v,&w,&f);
        addEdge(u,v,w,f);
    }
    int maxflow=0,mincost=0;
    while(spfa(s,t)){//如果能继续增广 
        maxflow+=flow[t];//增加最大流 
        mincost+=flow[t]*dis[t];//增加最小费用 
        int now=t;//设置当前点为汇点 
        while(now!=s){
            e[last[now]].w-=flow[t];
            e[last[now]^1].w+=flow[t];
            now=pre[now];
        }
    }
    printf("%d %d\n",maxflow,mincost);//输出答案 
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值