图-网络流-最小费用最大流

下面给网络流增加一个因素:费用。假设每条边除了有一个容量限制外,还有一个单位流量所需的费用(cost)。在总流量最大的前提下,总费用最小的流,即最小费用最大流。

在最小费用流问题中,平行边变得有意义了,可能会有两条从u到v 的弧,费用分别为1和2。在没有费用的情况下,可以把两者合并,但由于费用的出现,无法合并这两条弧。再如,若边(u,v)和(v,u)均存在,且费用都是负数。则“同时从u流向v和从v流量u”是个不错的注意。为了更方便的叙述算法,先假定图中不存在平行边和反向边。这样就可以用两个邻接矩阵cap和cost保存各边的容量和费用。为了允许反向增广,规定cap[v][u]=0并且cost[u][v],表示沿着(u,v)的相反方向增广时,费用减少cost[u][v]、

这里直接给出最小费用路算法。和Edmonds-Karp算法类似,但每次用Bellman-Ford算法而非BFS找增广路。只要初始流是该流量下的最小费用可行流。每次增广后的新流都是新流量下的最小费用流。另外,费用值是可负可正的。为了减少溢出的可能,总费用cost采用long long来保存。

首先是辅助宏的定义:

typedef long long int LL;
const int INF  = 0x3f3f3f3f;
const int maxn = 100 + 5;

边的定义:

struct Edge
{
    int from;
    int to;
    int cap;
    int flow;
    int cost;    //费用
    Edge(int u, int v, int f, int c, int w):from(u), to(v), flow(f), cap(c), cost(w){}
};
struct MCMF
{
    int n, m;
    vector<Edge> edges;     //边数的两倍
    vector<int> G[maxn];    //邻接表,G[i][j]表示结点i的第j条边在e数组的序号
    int a[maxn];            //当起点到i的可改进量
    int p[maxn];            //最短路树上的入弧编号
    bool inq[maxn];         //是否在队列中
    int d[maxn];            //最小费用,即最短路

    void init(int n)
    {
        this->n = n;
        edges.clear();
        for(int i = 0; i < n; i++)
            G[i].clear();
    }

    void AddEdge(int from, int to, int cap, int cost)
    {
        edges.push_back(Edge(from, to, 0, cap, cost));
        edges.push_back(Edge(to, from, 0, 0, -cost));             //反向弧
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }

    bool BellmanFord(int s, int t, int &flow, LL &cost)
    {
        int u, i;
        memset(d, INF, sizeof(d));
        memset(inq, false, sizeof(inq));
        d[s] = 0;
        p[s] = 0;
        a[s] = INF;

        queue<int> Q;
        Q.push(s);
        inq[s] = true;
        while(!Q.empty())
        {
            u = Q.front();
            Q.pop();
            inq[u] = false;
            for(i = 0; i < G[u].size(); i++)
            {
                Edge &e = edges[G[u][i]];
                if(e.cap > e.flow && d[e.to] > d[u] + e.cost)       //增广
                {
                    d[e.to] = d[u] + e.cost;
                    a[e.to] = min(a[u], e.cap - e.flow);
                    p[e.to] = G[u][i];
                    if(!inq[e.to])
                    {
                        Q.push(e.to);
                        inq[e.to] = true;
                    }
                }
            }
        }

        if(d[t] == INF)                                            //没有通路
            return false;
        flow += a[t];
        cost += (LL)d[t] * a[t];
        for(u = t; u != s; u = edges[p[u]].from)
        {
            edges[p[u]].flow += a[t];
            edges[p[u]^1].flow -= a[t];
        }

        return true;
    }

    //需要保证初始网络中没有负权圈
    int MincostMaxflow(int s, int t, int flow_limit, LL &cost)   //flow_limit:流量限制
    {
        int flow;

        flow = 0;
        cost = 0;
        while(flow < flow_limit && BellmanFord(s, t, flow, cost));

        return flow;
    }
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值