2017华为软件精英挑战赛之求解最小费用最大流
2017-04-08 23:06
567 查看
两个网络节点之间最多仅存在一条链路,链路上下行方向的网络总带宽相互独立,并且上下行方向的总带宽与网络租用费相同。例如对于网络节点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 = 正}
表格的形式看的更直观一些。
3.了解了上面的内容,就可以结合最小费用流算法求出最小费用,代码如下:
4.算出了最小费用我们还要求最大流,并输出数据流路径,可以用dfs深度遍历搜索,代码如下:
http://download.csdn.net/download/lzhf1122/9808461
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 |
#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
相关文章推荐
- 2017华为软件精英挑战赛之求解最小费用最大流
- 2017华为软件精英挑战赛之求解最小费用最大流
- 2017华为软件精英挑战赛之求解最小费用最大流
- 2017华为软件精英挑战赛之求解最小费用最大流
- 2017华为软件精英挑战赛之求解最小费用最大流
- [置顶] 2017华为软件精英挑战赛小结
- 2018年华为软件精英挑战赛求解思路
- 2017华为软件精英挑战赛总结
- 最小费用流算法不完全指南-2017华为软件精英挑战赛
- [置顶] 2017华为软件精英挑战赛小结
- 2018年华为软件精英挑战赛求解思路
- [置顶] 2017华为软件精英挑战赛小结
- 2018年华为软件精英挑战赛求解思路
- 2017华为软件精英挑战赛
- 2017华为软件精英挑战赛读文件和写文件源代码(C++)
- [置顶] 2017华为软件精英挑战赛系列二
- 2017华为软件精英挑战赛txt数据的读取(MATLAB实现)
- [置顶] 2017华为软件精英挑战赛系列三
- 2017华为软件精英挑战赛--总结
- 华为2017软件精英挑战赛 赛程回顾