• 最小费用最大流


      所谓最小费用最大流,就是给边再加一个单位流量的费用,

      每流过去单位流量, 就会花费这么多的费用    

    我们要求的就是在最大流下的最小费用

    Dinic+SPFA

     

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cctype>
    #include<algorithm>
    #include<queue>
    using namespace std;
    #define int long long
    #define olinr return
    #define _ 0
    #define love_nmr 0
    #define DB double
    #define inf 0x7ff
    inline int read()
    {
        int x=0,f=1;
        char ch=getchar();
        while(!isdigit(ch))
        {
            if(ch=='-')
                f=-f;
            ch=getchar();
        }
        while(isdigit(ch))
        {
            x=(x<<1)+(x<<3)+(ch^48);
            ch=getchar();
        }
        return x*f;
    }
    inline void put(int x)
    {
        if(x<0)
        {
            x=-x;
            putchar('-');
        }
        if(x>9)
            put(x/10);
        putchar(x%10+'0');
    }
    struct DIJ
    {
        int id;
        int dis;
        friend bool operator < (const DIJ &a,const DIJ &b)
        {
            return a.dis>b.dis;
        }
    };
    int s;
    int t;
    int n;
    int m;
    queue<int>q;
    struct node
    {
        int to;
        int dis;
        int nxt;
        int flow;
        int cap;
    }edge[105050];
    int head[10505];
    int cnt=-1;
    bool vis[10505];
    int change[10505];
    int road[10505];
    int cost[10505];
    int ans;
    int tot;
    int minn=0x7fffffff;
    inline void add(int from,int to,int dis,int cap,int flow)
    {
        cnt++;
        edge[cnt].cap=cap;
        edge[cnt].to=to;
        edge[cnt].nxt=head[from];
        edge[cnt].dis=dis;
        edge[cnt].flow=flow;
        head[from]=cnt;
    }
    inline bool dij()
    {
        memset(change,0x7f,sizeof change);
        memset(cost,0x7f,sizeof cost);
        cost[s]=0;
        q.push(s);
        vis[s]=true;
        while(!q.empty())
        {
            int tp=q.front();
            q.pop();
            vis[tp]=false;
            for(int i=head[tp];i!=-1;i=edge[i].nxt)
            {
                int go=edge[i].to;
                if(cost[go]>cost[tp]+edge[i].dis&&edge[i].cap>edge[i].flow)   //spfa
                {
                    cost[go]=cost[tp]+edge[i].dis;    //更新花费
                    road[go]=i;   //记录路径
                    change[go]=min(change[tp],edge[i].cap-edge[i].flow);  //残量取min
                    if(!vis[go])
                    {
                        vis[go]=true;
                        q.push(go);
                    }
                }
            }
        }
        if(change[t]>1e9) return false;
        return true;
    }
    inline void Dinic()
    {
        while(dij())
        {
            ans+=change[t];   //
            tot+=change[t]*cost[t];   //更新花费,流量*单价
            for(int i=t;i!=s;i=edge[road[i]^1].to)
            {
                edge[road[i]].flow+=change[t];  //更新
                edge[road[i]^1].flow-=change[t];
            }
        }
    }
    signed main()
    {
        n=read();
        m=read();
        s=read();
        t=read();
        memset(head,-1,sizeof head);
        for(int a,b,c,d,i=1;i<=m;i++)
        {
            a=read();
            b=read();
            c=read();
            d=read();
            minn=min(minn,c);
            add(a,b,d,c,0);
            add(b,a,-d,0,0);
        }
        Dinic();
        put(ans);
        putchar(' ');
        put(tot);
        olinr ~~(0^_^0)+love_nmr;
    }

     

    不过。。。。。众所周知,SPFA他死了。。。。。。。

    那么就得用dij了

    然而。。。。。TM他不能处理负权啊,那咋办?

    考虑加一个大数,最后再减回来?(可能炸long long QAQ)

    我们给每个点定义一个势h

    将转移从$dis_u=dis_v+w$ 改成$dis_u=dis_v+w+h_v-h_u$

    并保证$w+h_v-h_u ge 0$

    那么假如我们有一条$p_1---p_2---p_3---......p_n$的路径

    那么路径长度为$w_1+w_2+w_3+.......+w_n+(h_2-h_1)+(h_3-h_2)+......+(h_n-h_{n-1})=$

    后面的h就只剩起点与终点的h了,那么问题就解决了

    现在要考虑的是,h怎么定义

    我们把刚刚保证的式子变一下形

    $w+h_vge h_u$

    额  貌似。。。$w+dis_vge dis_u$

    这在最短路是对于所有边恒成立的

    所以我们把上一次的dis作为$h_i$每次让$h_i+=dis[i]$

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cctype>
    #include<algorithm>
    #include<queue>
    using namespace std;
    #define int long long
    #define olinr return
    #define _ 0
    #define love_nmr 0
    #define DB double
    #define inf 0x7ff
    inline int read()
    {
        int x=0,f=1;
        char ch=getchar();
        while(!isdigit(ch))
        {
            if(ch=='-')
                f=-f;
            ch=getchar();
        }
        while(isdigit(ch))
        {
            x=(x<<1)+(x<<3)+(ch^48);
            ch=getchar();
        }
        return x*f;
    }
    inline void put(int x)
    {
        if(x<0)
        {
            x=-x;
            putchar('-');
        }
        if(x>9)
            put(x/10);
        putchar(x%10+'0');
    }
    struct DIJ
    {
        int id;
        int dis;
        friend bool operator < (const DIJ &a,const DIJ &b)
        {
            return a.dis>b.dis;
        }
    };
    int s;
    int t;
    int n;
    int m;
    priority_queue<DIJ> q;
    struct node
    {
        int to;
        int dis;
        int nxt;
        int flow;
        int cap;
    }edge[105050];
    int head[10505];
    int cnt=-1;
    bool vis[10505];
    int change[10505];
    int road[10505];
    int cost[10505];
    int h[10505];
    int ans;
    int tot;
    int minn=0x7fffffff;
    inline void add(int from,int to,int dis,int cap,int flow)
    {
        cnt++;
        edge[cnt].cap=cap;
        edge[cnt].to=to;
        edge[cnt].nxt=head[from];
        edge[cnt].dis=dis;
        edge[cnt].flow=flow;
        head[from]=cnt;
    }
    inline bool dij()
    {
        memset(vis,false,sizeof vis);
        memset(change,0x7f,sizeof change);
        memset(cost,0x7f,sizeof cost);
        cost[s]=0;
        q.push((DIJ){s,0});
        while(!q.empty())
        {
            DIJ tp=q.top();
            q.pop();
            if(vis[tp.id]) continue;
            vis[tp.id]=true;
            for(int i=head[tp.id];i!=-1;i=edge[i].nxt)
            {
                int go=edge[i].to;
                if(cost[go]>cost[tp.id]+edge[i].dis+h[tp.id]-h[go]&&edge[i].cap>edge[i].flow)   //判断的时候带上势
                {
                    cost[go]=cost[tp.id]+edge[i].dis+h[tp.id]-h[go];   //带着势
                    road[go]=i;
                    change[go]=min(change[tp.id],edge[i].cap-edge[i].flow);
                    q.push((DIJ){go,cost[go]});
                }
            }
        }
        if(change[t]>1e9) return false;
        return true;
    }
    inline void Dinic()
    {
        while(dij())
        {
            ans+=change[t];
            tot+=change[t]*(cost[t]-h[s]+h[t]);   //只有起点终点影响tot
            for(int i=t;i!=s;i=edge[road[i]^1].to)
            {
                edge[road[i]].flow+=change[t];
                edge[road[i]^1].flow-=change[t];
            }
            for(int i=1;i<=n;i++)
                h[i]+=cost[i];   //别忘更新h
        }
    }
    signed main()
    {
        n=read();
        m=read();
        s=read();
        t=read();
        memset(head,-1,sizeof head);
        for(int a,b,c,d,i=1;i<=m;i++)
        {
            a=read();
            b=read();
            c=read();
            d=read();
            minn=min(minn,c);
            add(a,b,d,c,0);
            add(b,a,-d,0,0);
        }
        Dinic();
        put(ans);
        putchar(' ');
        put(tot);
        olinr ~~(0^_^0)+love_nmr;
    }
  • 相关阅读:
    整数反转
    两数之和
    设计模式-备忘录模式
    设计模式-迭代器模式
    设计模式-中介者模式
    设计模式-观察者模式
    C# OpenFileDialog和SaveFileDialog的常见用法
    SQL数据库表结构的修改(sql2005)
    C# 时间格式处理
    C# 集合类(四)
  • 原文地址:https://www.cnblogs.com/olinr/p/9593072.html
Copyright © 2020-2023  润新知