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

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

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

点我

扯远了...

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

有没有想起什么?

思考一下......

对,最短路径!

所以我们完全可以用已死的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;
}

 

posted @ 2019-05-13 21:09  一叶知秋‘  阅读(181)  评论(0编辑  收藏  举报