欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

题解 洛谷 P3381 【【模板】最小费用最大流】

程序员文章站 2022-04-15 17:32:56
发了网络流,再来一发费用流 能做费用流的,网络流自然做得来,但在这还是不要脸的安利一下自己的博客(里面也有网络流的题解): 点我 扯远了... 费用流,就是在不炸水管的情况下求源点到汇点的最小费用。 有没有想起什么? 思考一下...... 对,最短路径! 所以我们完全可以用已死的SPFA求出不炸水管 ......

发了网络流,再来一发费用流

能做费用流的,网络流自然做得来,但在这还是不要脸的安利一下自己的博客(里面也有网络流的题解):

扯远了...

费用流,就是在不炸水管的情况下求源点到汇点的最小费用。

有没有想起什么?

思考一下......

对,最短路径!

所以我们完全可以用已死的spfa求出不炸水管的最短路径(当然,实在有心理阴影的可以用dijkstra)。

如果你最短路径都不会,还是去 这儿 和 这儿

然后再一把增广路求出最大流与最小费用就好了(我觉得很ok)

献上本蒟蒻的代码:

#include<cstdio>
#define maxn 5050
#define maxm 50005
#define inf 0x3f3f3f3f
inline int read(){
    int r=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){r=(r<<3)+(r<<1)+c-'0';c=getchar();}
    return r*f;
}
int s,t,n,m,head[maxn],pre[maxn],dis[maxn],q[maxn];
bool vis[maxn];
int s_e;
struct e{
    int v,c,w,nxt;
}e[maxm*2];
struct max_fei{//本人喜欢结构体
    inline void a_e(int u,int v,int c,int w){
        e[s_e]=(e){v,c,w,head[u]};
        head[u]=s_e++;
    }
    inline void add(int u,int v,int c,int w){
        a_e(u,v,c,w);
        a_e(v,u,0,-w);
    }
    inline bool spfa(){
        for(int i=1;i<=n;i++){
            dis[i]=inf;
            vis[i]=false;
        }
        dis[s]=0;
        vis[s]=true;
        q[0]=s;
        int hd=0,tl=1;
        while(hd^tl){
            int u=q[hd++];//循环队列
            hd%=maxn;
            for(int i=head[u];i!=-1;i=e[i].nxt){
                int v=e[i].v;
                if(dis[v]>dis[u]+e[i].w&&e[i].c){//判断水管还能运水吗
                    dis[v]=dis[u]+e[i].w;//更新
                    pre[v]=i;//记录位置
                    if(vis[v])continue;//如果在队里,那就不进队
                    vis[v]=true;
                    q[tl++]=v;
                    tl%=maxn;
                }
            }
            vis[u]=false;
        }
        if(dis[t]==inf)return false;
        return true;
    }
    inline int min(int a,int b){//原谅我的手写min
        return a<b?a:b;
    }
    inline int end(int &flow){//flow求最大流
        int p,u,min=1e9,ans=0;
        for(u=t;u!=s;u=e[p^1].v){//因为开始值为0,可以用xor来找反边
            p=pre[u];//往前找
            min=min(min,e[p].c);//找全部经过水管都能流过的最大流
            
        }
        for(u=t;u!=s;u=e[p^1].v){
            p=pre[u];
            e[p].c-=min;
            e[p^1].c+=min;
            ans+=e[p].w*min;//加费用
        }
        flow+=min;//加最大流
        return ans;
    }
    inline int solve(int &flow){
        int ans=0;
        while(spfa()){
            ans+=end(flow);
        }
        return ans;
    }
}flow;
inline void work(){
    n=read();m=read();
    s=read();t=read();
    for(int i=1;i<=n;i++)head[i]=-1;//初始值为-1,方便xor
    for(int i=1;i<=m;i++){
        int u=read(),v=read(),c=read(),w=read();
        flow.add(u,v,c,w);
    }
    int flow=0;
    int ans=flow.solve(flow);
    printf("%d %d\n",flow,ans);
}
int main(){
    work();
    return 0;
}