P3381 【模板】最小费用最大流 (dijikstra优化)

题目描述

给出一个包含 nn 个点和 mm 条边的有向图(下面称其为网络) G=(V,E)G=(V,E),该网络上所有点分别编号为 1 \sim n1∼n,所有边分别编号为 1\sim m1∼m,其中该网络的源点为 ss,汇点为 tt,网络上的每条边 (u,v)(u,v) 都有一个流量限制 w(u,v)w(u,v) 和单位流量的费用 c(u,v)c(u,v)。

你需要给每条边 (u,v)(u,v) 确定一个流量 f(u,v)f(u,v),要求:

  1. 0 \leq f(u,v) \leq w(u,v)0≤f(u,v)≤w(u,v)(每条边的流量不超过其流量限制);
  2. \forall p \in \{V \setminus \{s,t\}\}∀p∈{V∖{s,t}},\sum_{(i,p) \in E}f(i,p)=\sum_{(p,i)\in E}f(p,i)∑(i,p)∈E​f(i,p)=∑(p,i)∈E​f(p,i)(除了源点和汇点外,其他各点流入的流量和流出的流量相等);
  3. \sum_{(s,i)\in E}f(s,i)=\sum_{(i,t)\in E}f(i,t)∑(s,i)∈E​f(s,i)=∑(i,t)∈E​f(i,t)(源点流出的流量等于汇点流入的流量)。

定义网络 GG 的流量 F(G)=\sum_{(s,i)\in E}f(s,i)F(G)=∑(s,i)∈E​f(s,i),网络 GG 的费用 C(G)=\sum_{(i,j)\in E} f(i,j) \times c(i,j)C(G)=∑(i,j)∈E​f(i,j)×c(i,j)。

你需要求出该网络的最小费用最大流,即在 F(G)F(G) 最大的前提下,使 C(G)C(G) 最小。

输入格式

输入第一行包含四个整数 n,m,s,tn,m,s,t,分别代表该网络的点数 nn,网络的边数 mm,源点编号 ss,汇点编号 tt。

接下来 mm 行,每行四个整数 u_i,v_i,w_i,c_iui​,vi​,wi​,ci​,分别代表第 ii 条边的起点,终点,流量限制,单位流量费用。

输出格式

输出两个整数,分别为该网络的最大流 F(G)F(G),以及在 F(G)F(G) 最大的前提下,该网络的最小费用 C(G)C(G)。

输入输出样例

输入 #1复制

4 5 4 3
4 2 30 2
4 3 20 3
2 3 20 1
2 1 30 9
1 3 40 5

输出 #1复制

50 280

说明/提示

对于 100\%100% 的数据,1 \leq n \leq 5\times 10^31≤n≤5×103,1 \leq m \leq 5 \times 10^41≤m≤5×104,1 \leq s,t \leq n1≤s,t≤n,u_i \neq v_iui​=vi​,0 \leq w_i,c_i \leq 10^30≤wi​,ci​≤103,且该网络的最大流和最小费用 \leq 2^{31}-1≤231−1。

输入数据随机生成。

【模板】最小费用最大流 - 洛谷

因为dijkstra不能求有负边的图,我们给每个边附上一个值,使得附上值后的所有边的权值均为正就能进行dijkstra了。(参照白皮书)

#include<iostream>
#include<queue>
#include<algorithm>
#include<cstring>
#include<cstdio>
#define int long long
#define INF 0x3f3f3f3f
using namespace std;
typedef pair<int,int> P;
int n,m,source,sink;
int mincost,maxflow;
int head[(int)1e3*5+5];
int h[(int)1e3*5+5];
int dis[(int)1e3*5+5];
int prevv[(int)1e3*5+5];
int preve[(int)1e3*5+5];
struct node{
    int to;
    int w;
    int nxt;
    int dis;
}edge[(int)1e5+5];
int cnt;
void add_edge(int from,int to,int w,int dis)//w是容量
{
    edge[cnt].to=to;
    edge[cnt].w=w;
    edge[cnt].dis=dis;
    edge[cnt].nxt=head[from];
    head[from]=cnt++;
}
bool dijkstra()
{
    for(int i=0;i<=n;i++)dis[i]=INF;
    priority_queue<P,vector<P>,greater<P> > que;
    dis[source]=0;
    que.push(P(0,source));
    while(!que.empty())
    {
        P p=que.top();
        que.pop();
        int v=p.second;
        if(p.first>dis[v])continue;
        for(int i=head[v];~i;i=edge[i].nxt)
        {
            int to=edge[i].to,cost=edge[i].dis;
            if(edge[i].w>0&&dis[to]>dis[v]+cost+h[v]-h[to])
            {
                dis[to]=dis[v]+cost+h[v]-h[to];
                prevv[to]=v;
                preve[to]=i;
                que.push(P(dis[to],to));
            }
        }
    }
    if(dis[sink]==INF)return false;
    return true;
}
void MCMF() {
    while (dijkstra())
    {
        for(int i=1;i<=n;i++)h[i]+=dis[i];
        int flow=INF;
        for(int i=sink;i!=source;i=prevv[i])
        {
            flow=min(flow,edge[preve[i]].w);
        }
        mincost+=flow*h[sink];
        maxflow+=flow;
        for(int i=sink;i!=source;i=prevv[i])
        {
            edge[preve[i]].w-=flow;
            edge[preve[i]^1].w+=flow;
        }
    }
    //cout<<maxflow<<" "<<mincost;
    printf("%ld %ld",maxflow,mincost);
}
signed main()
{
    memset(head,-1,sizeof(head));
    scanf("%ld%ld%ld%ld",&n,&m,&source,&sink);
    int x,y,z,k;
    for(int i=0;i<m;i++)
    {
        //cin>>x>>y>>z>>k;
        scanf("%ld%ld%ld%ld",&x,&y,&z,&k);
        add_edge(x,y,z,k);
        add_edge(y,x,0,-k);
    }
    MCMF();
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值