洛谷 P3381 【模板】最小费用最大流 Dinic+Spfa

题目连接:

https://www.luogu.com.cn/problem/P3381

参考博客:

https://blog.csdn.net/A_Comme_Amour/article/details/79356220

这个博客关于网络流的知识总结的很全面,建议查看

算法:1:Dinic+Spfa

思路:

1:现在我们想象假如我们有一个流量网络,现在每个边除了流量,现在还有一个单位费用(按最短路),这条边的费用相当于它的单位费用乘上它的流量,(因为要多次bfs寻找源点s到汇点t的路径,每一次都可以找到一条流,找到的这条流的流量是确定的,但是这条流的路线却是可以变的,这就是一个求最短路的过程,把求最小费用转化为求最短路径用Spfa算法)我们要保持最大流的同时,还要保持边权最小,这就是最小费用最大流问题 
2:因为在一个网络流图中,最大流量只有一个,但是“流法”有很多种,每种不同的流法所经过的边不同因此费用也就不同,所以需要用到最短路算法 
3:每次增广的费用就是这次增广的最短路*这次的流量
4:关于Spfa:就是把Dinic中的bfs改成spfa,再求最大流的过程中最小费用流也就求出来了
5:注意:有许多初始化的地方容易漏易错

#include 

using namespace std;
const int maxn=5e3+1,maxm=1e5+1;
int n,m,s,t,tot=1,head[maxn],dis[maxn],flow[maxn],pre[maxn],last[maxn],maxflow,mincost;
//dis最小花费;pre每个点的前驱;last每个点的所连的前一条边;flow源点到此处的流量
bool vis[maxn];
queueq;

struct edge
{
    int to,next,w,dis;//w流量 dis花费
}e[maxm];

void addedge(int x,int y,int w,int dis)
{
    e[++tot].to=y;
    e[tot].w=w;
    e[tot].dis=dis;
    e[tot].next=head[x];
    head[x]=tot;
}

bool spfa(int s,int t)
{
    memset(dis,0x7f,sizeof(dis));
    memset(flow,0x7f,sizeof(flow));
    memset(vis,0,sizeof(vis));
    q.push(s);vis[s]=1;dis[s]=0;pre[t]=-1;
    while(!q.empty())
    {
        int now=q.front();q.pop();
        vis[now]=0;
        for(int i=head[now];i!=-1;i=e[i].next)
        {
            int y=e[i].to;
            if(e[i].w>0&&dis[y]>dis[now]+e[i].dis)//相当于求最短路
            {
                dis[y]=dis[now]+e[i].dis;
                pre[y]=now;
                last[y]=i;
                flow[y]=min(flow[now],e[i].w);
                if(!vis[y])
                {
                    vis[y]=1;q.push(y);
                }
            }
        }
    }
    return pre[t]!=-1;
}

void dfs()
{
    int now=t;
    maxflow+=flow[t];
    mincost+=flow[t]*dis[t];
    while(now!=s)//从汇点一直回溯到源点
    {
        e[last[now]].w-=flow[t];
        e[last[now]^1].w+=flow[t];
        now=pre[now];
    }
}

int main()
{
    ios::sync_with_stdio(0);
    scanf("%d %d %d %d",&n,&m,&s,&t);
    memset(head,-1,sizeof(head));//初始化
    for(int i=1;i<=m;i++)
    {
        int x,y,w,dis;scanf("%d %d %d %d",&x,&y,&w,&dis);
        addedge(x,y,w,dis);
        addedge(y,x,0,-dis); //反边的流量为0,花费是相反数
    }
    while(spfa(s,t))dfs();
    printf("%d %d\n",maxflow,mincost);
    return 0;
}

 

你可能感兴趣的:(二分图+网络流,洛谷,P3381,【模板】最小费用最大流,最小费用最大流,Dinic+Spfa)