2017华为软件精英挑战赛之求解最小费用最大流

来源:互联网 发布:c语言二级文件系统 编辑:程序博客网 时间:2024/05/18 00:17

   两个网络节点之间最多仅存在一条链路,链路上下行方向的网络总带宽相互独立,并且上下行方向的总带宽与网络租用费相同。例如对于网络节点A与B之间的链路,该条链路上的总带宽为5,单位租用费为1,则表示A->B、B->A两个方向上的网络总带宽分别为5,并且租用费均为1。如果某条数据流在该链路A->B方向的占用带宽为3,那么该数据流在该链路的租用费为3,并且该链路A->B方向的剩余可用带宽为2。而B->A方向的剩余可用带宽不受该数据流的影响,仍为5。

1. 如图一,假设经过A与B有两条数据流,分别为:

C->A->B->E,流量flow = 3;

F->B->A->D,流量flow = 2;

图一:

那么在A与B之间的这条链路的花费为(3+2)*1 = 5;

再来看图二,图中有三条数据流,分别为:

C->A->D,流量flow = 2;

C->A->B->E,流量flow = 1;

F->B->E,流量flow = 2;

图二:


2与图1相比,效果一样,但在A与B之间链路的花费仅为1*1 = 1;

因此,如果链路的上下行方向都有流量,会增加成本,造成浪费。


2.问题来了,怎么避免两个方向都有流量呢。为方便说明,举个例子。

看下面内容之前,务必先了解一下最大流和最小费用的基本算法。


设A->B、B->A两个方向上容量都为5,并且单位流量费用均为1。总花费为zongcost。

A->B方向的初始值:ABflow = 0;ABcap = 5;ABcost = 1;

B->A方向的初始值:BAflow = 0;BAcap = 5;BAcost = 1;

第一步:A->B方向流过3个单位流量。

Zongcost = 3 * ABcost =3;

A->B方向:ABflow = 3;ABcap = 5;ABcost = 1;

ABcap - ABflow 等于2,说明A->B方向还可以流2个单位流量。

B->A方向:BAflow = -3;BAcap = 0;BAcost = -1;

   把BAcap 设为0,BAcap - BAflow 等于3,表示B->A方向可以流过3个单位流量,而且单价还是负的,这样,下次B->A方向有流量经过时就可以先抵消掉在A->B方向上流量花费的费用。

第二步:B->A方向流过3个单位的流量。

Zongcost = Zongcost + 3 * BAcost =0;

B->A方向:BAflow = -3+3 = 0;BAcap = 5;BAcost = 1;

可以发现这时B->A方向和初始状态的值时一样的,可以流过5个单位的流量,单价为1.

A->B方向:ABflow = 3 - 3 = 0;ABcap = 5;ABcost = 1;

也初始状态一样。

第三步:B->A方向流过1个单位的流量。

Zongcost = Zongcost + 1 * BAcost =1;

B->A方向:BAflow = 0+1 = 0;BAcap = 5;BAcost = 1;

A->B方向:ABflow = 0- 1 = -1;ABcap = 0;ABcost = -1;

通过总结以上三个步骤可以发现规律:

if(flow<0)   {cap = 0; cost = 负;}

else  { cap = 5; cost = 正}

表格的形式看的更直观一些。

 

A->B

B->A

 

ABflow

ABcap

ABcost

BAflow

BAcap

BAcost

初始值

0

5

1

0

5

1

第一步后

3

5

1

-3

0

-1

第二步后

0

5

1

0

5

1

第三步后

-1

0

-1

1

5

1


3.了解了上面的内容,就可以结合最小费用流算法求出最小费用,代码如下:

#include <string>#include <iostream>#include <stack>#include <vector>#include <queue>using namespace std;const int INF = 0x3f3f3f3f;int maxData = 0x3fffffff;stack<int> st; //为了显示输出路径queue<int> myqueue;int pre[MaxNum];//标记路径上当前节点的前驱//边的结构体struct ArcNodeType{    int adjvex;    int cap;    int flow;    int cost;    int fixcap;    int fixcost;    ArcNodeType *nextarc;};void AlterFlow(int s,int t,int increase) {    while (!st.empty())       //栈清空        st.pop();    int k = t;          //利用前驱寻找路径    st.push(k);    ArcNodeType *p;    while (k != s) {        int last = pre[k];        //改变正向边的流量        p = VexList[last].firstarc;        while (p->adjvex != k)//直到p->data为key为止            p = p->nextarc;//扫描下一结点        p->flow += increase;        if (p->flow < 0) {            p->cap= 0;            p->cost = -(p->fixcost);        }        else {            p->cap= p->fixcap;            p->cost = p->fixcost;        }        //改变反向边的流量,反向边不一定存在        p = VexList[k].firstarc;        while (p && p->adjvex != last)            p = p->nextarc;//扫描下一结点        if (p) //  存在并找到        {            p->flow -= increase;            if (p->flow < 0) {                p->cap= 0;                p->cost = -(p->fixcost);            }            else {                p->cap = p->fixcap;                p->cost = p->fixcost;            }        }        k = last;        st.push(k);    }}int BellmanFord(int src, int des,int maxData,long &cost){    int d[MaxNum];//Bellman-Ford    int a[MaxNum];//可改进量    int inq[MaxNum];//是否在队列中    while (!myqueue.empty())       //队列清空        myqueue.pop();    for (int i = 0; i<1502; i++)    {d[i] = INF; inq[i] = 0; }         //本来就很大    a[src] = maxData;    d[src] = 0;    inq[src] = 1;    myqueue.push(src);    while (!myqueue.empty())    {        int index = myqueue.front();        myqueue.pop();  //zk 遍历过就出栈不占地方        inq[index] = 0;       //不在了        ArcNodeType *p=VexList[index].firstarc;        while(p)        {            int adjvex=p->adjvex;            if(d[adjvex] > d[index] + p->cost && p->cap > p->flow)  //松弛操作            {                d[adjvex] = d[index] + p->cost;                pre[adjvex] = index; //记录前驱                a[adjvex] = min(p->cap - p->flow, a[index]);//更新可改                if (!inq[adjvex])                {                    myqueue.push(adjvex);   //防止该点遍历两次                    inq[adjvex] = 1;                }            }            p=p->nextarc;        }    }    if (d[des] == INF)//仍为初始时候的INF,s-t不连通,失败退出        return 0;    cost += (long)d[des] * a[des];//d[t]一方面代表了最短路长度,另一方面代表了这条最短路的单位费用的大小    return a[des];}//调用该函数就可以得到最小花费costMin_Cost(int s,int t,long &cost){    cost = 0;    int f = 0;    for(;;)    {        for(int i=0;i<1502;i++) pre[i]=-1;        pre[s]=0;  //0也代表遍历到        f=BellmanFord(s,t,INF,cost);        if(f==0)            break;        AlterFlow(s,t,f);    }}

4.算出了最小费用我们还要求最大流,并输出数据流路径,可以用dfs深度遍历搜索,代码如下:

stack<int> st; //为了显示输出路径int pre[MaxNum];//标记在这条路径上当前节点的前驱// 用来搜索路径int dfs(int s,int t,int f){    if(s==t)    {        return f;    }    ArcNodeType *p=VexList[s].firstarc;    while(p)    {        int adjvex=p->adjvex;        if(pre[adjvex] == -1 && p->flow>0)        {            pre[adjvex] = s; //记录前驱            int d= dfs(adjvex,t,min(f,p->flow));            if(d>0)            {                p->flow-=d;                return d;            }        }        p=p->nextarc;    }    return 0;}void DisplayRoad(int s,int t){    while (!st.empty())       //栈清空        st.pop();    int k = t;          //利用前驱寻找路径    st.push(k);    ArcNodeType *p;    while (k != s) {        int last = pre[k];        k = last;        st.push(k);    }    int j = st.size();    for (int i = 0; i < j; i++)    {        if (st.top() == s || st.top() == t)        {            st.pop();            continue;        }        else        {            cout<<st.top()<<" ";        }        st.pop();    }}int max_flow(int s,int t){    int flow=0;    roadnum = 0; //每次路径数归零    int f = 0;    for(;;)    {        for (int i = 0; i < 1502; i++) pre[i] = -1;        pre[s] = 0;  //0也代表遍历到        f = dfs(s, t, INF);        if (f == 0)            return flow;        flow += f;        roadnum++;        DisplayRoad(s,t);    }}
如果想获得更完整的代码,可以到此处下载。

http://download.csdn.net/download/lzhf1122/9808461


1 0
原创粉丝点击