您的位置:首页 > 其它

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

2017-04-08 23:06 417 查看
   两个网络节点之间最多仅存在一条链路,链路上下行方向的网络总带宽相互独立,并且上下行方向的总带宽与网络租用费相同。例如对于网络节点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];

}
//调用该函数就可以得到最小花费cost
Min_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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: