图论---最小费用流算法的实现

开始编程前分析设计思路和程序的整体的框架,以及作为数学问题的性质:

程序流程图:

数学原理:

        基于Bellman-Ford算法的最小费用最大流,通过不断松弛图中的边来更新节点之间的最短距离,除了考虑边的容量和费用外,还需要考虑流量的限制,对所有边进行松弛操作。在重复地计算中,已计算得到正确的距离的边的数量不断增加,直到所有边都计算得到了正确的路径。

时间复杂度分析:

        在Bellman-Ford算法中,外层循环执行了n次,其中n是节点的数量。对于每个节点,内层循环会遍历该节点的所有邻接边,假设每个节点的平均度数为d,则内层循环最多执行d次。总的时间复杂度为O(n * d),即O(n^2)

源代码:

#include <iostream>
#include <queue>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;
#define N 1010
typedef struct edge
{
    int from; // 起点
    int to; // 终点
    int cap; // 容量
    int flow; // 流量
    int cost; // 费用
}Edge;
//用于存储每个节点的最短距离
//用于存储每个节点的前驱节点
//用于存储每个节点到起点的距离
//用于标记每个节点是否在队列中
int a[N], p[N], d[N], inq[N];
vector<Edge> edges;
//用于存储每个节点的邻接表(边的编号)
vector<int> G[N];
int n, m, s, t, T;
int fflow = 0, total_cost = 0;
bool Bellman_Ford(int s, int t)
{
    memset(a, 0, sizeof(a));
    memset(p, 0, sizeof(p));
    memset(inq, 0, sizeof(inq));
    for (int i = 1; i <= n; i++)
    {
        d[i] = 1000000;
    }
    a[s] = 10000;
    d[s] = 0;
    queue<int> Q;
    Q.push(s);
    p[s] = 0;
    inq[s] = 1;
    while (!Q.empty())
    {
        int x = Q.front(); Q.pop(); inq[x] = 0;
        for (int i = 0; i < G[x].size(); i++)
        {
            Edge& e = edges[G[x][i]];
            //若该点能够被松弛,下面更新a,p,d,并判断该点能否入队
            if (e.cap > e.flow && d[e.to] > d[x] + e.cost)
                {
                    d[e.to] = d[x] + e.cost;
                    p[e.to] = G[x][i];
                    a[e.to] = min(a[x], e.cap - e.flow);
                    if (inq[e.to] == 0)
                    {
                        Q.push(e.to); inq[e.to] = 1;
                    }
                }
        }
    }
    //Bellman_Ford在当前残量图中松弛完毕后,下面更新其所涉及边的流量变化。
    //首先查看d[t]是否发生了变化,若不变,则说明已无增广路
    if (d[t] == 1000000)
    {
        return false;
    }
    else//若d[t]的值得到更新,则考虑更新残量图
    {
        fflow += a[t];
        total_cost += d[t] * a[t];
        for (int i = t; i != s; i = edges[p[i]].from)
        {
            edges[p[i]].flow += a[t];
            edges[p[i]^1].flow -= a[t];
        }
        return true;
    }
}
int main()
{
    int a, b, c, co;
    cout << "总顶点  边数  源点  汇点" << endl;
    cin >> n >> m >> s >> t;
    cout << "前 后 容量 费用" << endl;
    for (int i = 1; i <= m; i++)
    {
        cin >> a >> b >> c >> co;
        Edge e;
        e.from = a; e.to = b; e.cap = c; e.cost = co; e.flow = 0;
        edges.push_back(e);
        e.from = b; e.to = a; e.cap = 0; e.cost = -co; e.flow = 0;
        edges.push_back(e);
        int num = edges.size();
        G[a].push_back(num - 2);
        G[b].push_back(num - 1);
    }
    while (Bellman_Ford(s, t));
    cout << "流量:" << fflow << endl;
    cout << "总费用:" <<total_cost << endl;

    system("pause");
    return 0;
}

测试用例:(图结构)

输出结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值