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

原题链接:https://www.luogu.org/problemnew/show/P3381

【模板】最小费用最大流

题目描述

如题,给出一个网络图,以及其源点和汇点,每条边已知其最大流量和单位流量费用,求出其网络最大流和在最大流情况下的最小费用。

输入输出格式
输入格式:

第一行包含四个正整数N、M、S、T,分别表示点的个数、有向边的个数、源点序号、汇点序号。

接下来M行每行包含四个正整数ui、vi、wi、fi,表示第i条有向边从ui出发,到达vi,边权为wi(即该边最大流量为wi),单位流量的费用为fi。

输出格式:

一行,包含两个整数,依次为最大流量和在最大流量情况下的最小费用。

输入输出样例
输入样例#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

说明

时空限制:1000ms,128M

(BYX:最后两个点改成了1200ms)

数据规模:

对于30%的数据:N<=10,M<=10

对于70%的数据:N<=1000,M<=1000

扫描二维码关注公众号,回复: 1865216 查看本文章

对于100%的数据:N<=5000,M<=50000

样例说明:

如图,最优方案如下:

第一条流为4–>3,流量为20,费用为3*20=60。

第二条流为4–>2–>3,流量为20,费用为(2+1)*20=60。

第三条流为4–>2–>1–>3,流量为10,费用为(2+9+5)*10=160。

故最大流量为50,在此状况下最小费用为60+60+160=280。

故输出50 280。

题解

现在才学费用流。。。我太菜了。

建图和最大流差不多,反向边的单位费用为负。

大概就是每次 S P F A 找一条单位费用之和最小的路径,顺带计算出到达每个点的流量以及记录路径。

然后从终点开始顺着路径往起点跳,沿路修改流量。

重复上述过程,直到起点和终点不再联通,不断累计答案。

代码
#include<bits/stdc++.h>
using namespace std;
const int M=5e3+5;
struct sd{int to,fl,val;}ed[M*20];
int n,m,start,end,id,ans1,ans2,dis[M],minf[M],pre[M];
bool vis[M];
vector<int>mmp[M];
queue<int>dui;
void add(int u,int v,int w,int f)
{
    mmp[u].push_back(id);ed[id++]=(sd){v,w,f};
    mmp[v].push_back(id);ed[id++]=(sd){u,0,-f};
}
bool spfa(int s,int t)
{
    fill(dis+1,dis+1+n,INT_MAX);
    memset(vis,0,sizeof(vis));
    dui.push(s);dis[s]=0;vis[s]=1;minf[s]=0x3f3f3f3f;
    int f,to,fl,hh,val;
    while(!dui.empty())
    {
        f=dui.front();dui.pop();
        vis[f]=0;
        for(int i=mmp[f].size()-1;i>=0;--i)
        {
            hh=mmp[f][i];to=ed[hh].to;fl=ed[hh].fl;val=ed[hh].val;
            if(fl>0&&dis[to]>dis[f]+val)
            {
                dis[to]=dis[f]+val;
                minf[to]=min(minf[f],fl);
                pre[to]=hh;
                if(!vis[to])dui.push(to),vis[to]=1;
            }
        }
    }
    return dis[t]!=INT_MAX;
}
void up(int s,int t)
{
    int v=t,hh;
    while(v!=s)
    {
        hh=pre[v];
        ed[hh].fl-=minf[t];
        ed[hh^1].fl+=minf[t];
        v=ed[hh^1].to;
    }
    ans1+=minf[t];
    ans2+=minf[t]*dis[t];
}
void in()
{
    int a,b,c,d;
    scanf("%d%d%d%d",&n,&m,&start,&end);
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d%d%d",&a,&b,&c,&d);
        add(a,b,c,d);
    }
}
void ac()
{
    while(spfa(start,end))up(start,end);
    printf("%d %d\n",ans1,ans2);
}
int main(){in();ac();}

猜你喜欢

转载自blog.csdn.net/shadypi/article/details/80835047
今日推荐