2017华为软挑——最小费用最大流(MCMF)

来源:互联网 发布:gta5 日式女孩捏脸数据 编辑:程序博客网 时间:2024/05/16 06:43

1. 概述

1.1 最小费用最大流

今年的华为软件精英挑战赛是要在一张给定的流量网络中,找到合适服务器部署地点、最佳路由路径使得服务器到达消费节点的费用在满足流量需求的时候费用最小。因而在服务器给定的情况下就变成了,最小费用最大流问题了。


首先最小费用最大流问题:已知容量网络D=(V,A,C),每条弧(Vi,Vj)除了已给出容量Cij 外,还给出了单位流量的传输费用Bij ≥0,记作D=(V,A,C,B),其中Bij ∈B。要在费用、容量网络D中寻找Vs—>Vt的最大流f,且使流的总传输费用最小



对于最大流问题:
最大流的求法就是在容量网络上从某个可行流出发,设法找到一条从Vs—>Vt的增广链,然后沿着此增广链调整流量,作出新的流量增大了的可行流。在这个新的可行流基础上再寻找它的增广链。如此反复进行,直至再找不出增广链时,就得到了该网络的最大流。

最小费用最大流问题:
就先找一个最小费用可行流,再找出关于该可行流的最小费用增广链,沿此链调整流量,则得到一个新的流量增大了的最小费用流,然后对新的最小费用流重复上述方法,一直调整到网络的最大流出现为止,便得到了所考虑网络的最小费用最大流。
具体的原理性说明可以参考这里。

1.2 最短路算法
最短路算法在本赛题中占有至关重要的作用,算法运行的效率和速度对比赛成绩影响很大。(对于网络节点数目为800的图在我的本子上要跑200多ms,但是大佬写的借鉴ZKW的最短路算法,效率不知道高多少倍去了-_-||)。
SPFA算法估计是使用得比较多的方法了。SPFA的原理其实与Dijkstra原理上相似。只是它使用了一个队列去维护搜索最短路,减少了冗余。具体的原理的话这里就不再多赘述。

2. 编码

网络图数据结构:
#define MAX_NET_NODE 1000       //最大网络节点#define MAX_CLIENT_NODE 500     //最大消费节点//网络中的网络节点的输入和输出节点struct NODE{    std::vector<int> innode;    //指向该节点的节点    std::vector<int> outnode;   //从该节点值出去的节点};NODE net_node[MAX_NET_NODE];    //网络节点的存储//网络中的消费节点的输入和带宽需求struct CLIENT_NODE{    std::vector<int> innode;    //指向该节点的节点    int bandwidth;   //从该节点值出去的节点};CLIENT_NODE client_node[MAX_CLIENT_NODE];   //消费节点的存储int client_innode[MAX_NET_NODE];   //存储与消费节点相连的节点int note_to_client[MAX_NET_NODE];   //根据与消费节点相连的节点找到消费节点int needed_flow=0;                  //在本case中需要的总流量//每条边的属性:总带宽,带宽价格struct EDGE_ATRRIBUTE{    int bandwidth;  //每条边的总带宽    int bandcost;    //每1G带宽需要的费用};EDGE_ATRRIBUTE net_edge[MAX_NET_NODE][MAX_NET_NODE];    //存储图数据的矩阵
最小费用最大流(MCMF)(添加了SLF和LLL(-_-||)):
#define INFINITE 1 << 26#define MAX_NODE 1005//#define MAX_EDGE_NUM 40005struct Edge{    int to;    int vol;    int cost;    int next;};Edge gEdges[MAX_EDGE_NUM];int gHead[MAX_NODE];//int gPre[MAX_NODE];//int gPath[MAX_NODE];//路径int gDist[MAX_NODE];//距离int gEdgeCount;////////////////////////////////////*最小费用最大流 start*/////////////////////////////////////////////////void my_search_init(std::vector<int> server_pos){path.clear();//清空路径信息//min_server.clear();//清空服务器的数组//初始化数据for(int i=0; i<MAX_NODE; i++){gHead[i] = -1;gPre[i] = -1;gPath[i] = -1;gDist[i] = INFINITE;} gEdgeCount = 0;//加普通节点入边#ifdef MYDEBUG    cout << "普通网络节点的边初始化:" << endl;#endifint start(net_node_num);//超级起点int end(net_node_num+1);//超级终点#ifdef MYDEBUGint edge_count(0);#endiffor(int i=0; i<net_node_num; i++){int temp_start(i);if(CheckIsServer(i, server_pos))//是服务器节点{//加边#ifdef MYDEBUGcout << "服务器节点:" << i << endl;#endiffor(unsigned int j=0; j<net_node[temp_start].outnode.size(); j++){            int temp_end = net_node[temp_start].outnode[j];//if(CheckIsServer(temp_end, server_pos)) continue;//如果是服务器节点跳过            int bandwidth = net_edge[temp_start][temp_end].bandwidth;int cost = net_edge[temp_start][temp_end].bandcost;#ifdef MYDEBUG    cout << temp_start << "======>" << temp_end << " , " << bandwidth << " , " << cost << endl;cout << temp_end << "======>" << temp_start << " , " << bandwidth << " , " << cost << endl;edge_count++;edge_count++;#endifInsertEdge(temp_start, temp_end, bandwidth, cost);InsertEdge(temp_end, temp_start, bandwidth, cost);}for(unsigned int j=0; j<net_node[temp_start].innode.size(); j++){            int temp_end = net_node[temp_start].innode[j];//if(CheckIsServer(temp_end, server_pos)) continue;//如果是服务器节点跳过            int bandwidth = net_edge[temp_start][temp_end].bandwidth;int cost = net_edge[temp_start][temp_end].bandcost;#ifdef MYDEBUG    cout << temp_start << "======>" << temp_end << " , " << bandwidth << " , " << cost << endl;cout << temp_end << "======>" << temp_start << " , " << bandwidth << " , " << cost << endl;edge_count++;edge_count++;#endifInsertEdge(temp_start, temp_end, bandwidth, cost);InsertEdge(temp_end, temp_start, bandwidth, cost);}#ifdef MYDEBUGcout << "服务器节点:" << i << endl;#endifcontinue;}        for(unsigned int j=0; j<net_node[temp_start].outnode.size(); j++){            int temp_end = net_node[temp_start].outnode[j];if(CheckIsServer(temp_end, server_pos)) continue;//如果是服务器节点跳过            int bandwidth = net_edge[temp_start][temp_end].bandwidth;int cost = net_edge[temp_start][temp_end].bandcost;#ifdef MYDEBUG    cout << temp_start << "======>" << (temp_end) << " , " << bandwidth << " , " << cost << endl;cout << temp_end << "======>" << temp_start << " , " << bandwidth << " , " << cost << endl;edge_count++;edge_count++;#endifInsertEdge(temp_start, temp_end, bandwidth, cost);InsertEdge(temp_end, temp_start, bandwidth, cost);}}//加入超级起点节点的边#ifdef MYDEBUG    cout << "超级起点节点的边初始化:" << endl;#endiffor(unsigned int i=0; i<server_pos.size(); i++){int temp_end = server_pos[i];#ifdef MYDEBUG    cout << start << "======>" << temp_end << " , " << needed_flow << " , " << 0 << endl;edge_count++;#endifInsertEdge(start, temp_end, MAXINT, 0);}//加入超级终点节点的边#ifdef MYDEBUG    cout << "超级终点节点的边初始化:" << endl;#endiffor(int i=0; i<client_node_num; i++){int temp_start = client_node[i].innode[0];int bandwidth = client_node[i].bandwidth;#ifdef MYDEBUG    cout << temp_start << "======>" << end << " , " << bandwidth << " , " << 0 << endl;edge_count++;#endifInsertEdge(temp_start, end, bandwidth, 0);}#ifdef MYDEBUG    cout << "加入的总边数:" << edge_count << endl;#endif}bool CheckIsServer(int node_id, std::vector<int> server_pos){int size(server_pos.size());if (0==size) return false;for(int i=0; i<size; i++){if(node_id == server_pos[i]) return true;}return false;}//求出最小费用最大流int MinCostFlow(int s, int t, int& flow){    int cost = 0;path.clear();    while (Spfa(s, t)){        int f = INFINITE;std::vector<int> temp_path;        for (int u = t; u != s; u = gPre[u]){            if (gEdges[gPath[u]].vol < f)                f = gEdges[gPath[u]].vol;temp_path.push_back(gPre[u]);        }//找到增益的最小流        //cout << "increaseed flow:" << f << endl;temp_path.push_back(f);        flow += f;        int temp = cost;        //cost += gDist[t] * f;        for (int u = t; u != s; u = gPre[u]){            gEdges[gPath[u]].vol -= f;   //正向边容量减少            //gEdges[gPath[u]^1].vol += f; //反向边容量增加cost += gEdges[gPath[u]].cost*f;        }//设置路中的容量变化        temp_path.push_back(cost-temp);//记录这条路的价格path.push_back(temp_path);if(flow >= needed_flow)//找到需要的流量就可以了,不用找完所有的曾广路线return cost;    }    return cost;}//假设图中不存在负权和环,SPFA算法找到最短路径/从源点s到终点t所经过边的cost之和最小的路径bool Spfa(int s, int t){for(int i=0; i<(net_node_num+2); i++){gPre[i] = -1;gDist[i] = INFINITE;}bool isfind = false;//是否找到元素的标志位    gDist[s] = 0;    //int sum(0);//LLL中计算总的和的变量    std::deque<int> Q;    Q.push_back(s);    while (!Q.empty()){//由于不存在负权和环,因此一定会结束        int u = Q.front();        Q.pop_front();        //LLL//while (true && !Q.empty())//{//int dist = Q.front();//队首元素//if (gDist[dist]*Q.size() > sum){Q.pop_front(); Q.push_back(dist);}//else{break;}        //}if (isfind){break;}        for (int e = gHead[u]; e != -1; e = gEdges[e].next){            int v = gEdges[e].to;            if (gEdges[e].vol > 0 && (gDist[u] + gEdges[e].cost) < gDist[v]){                gDist[v] = gDist[u] + gEdges[e].cost;                gPre[v] = u; //前一个点                gPath[v] = e;//该点连接的前一个边                //sum += gDist[v];//LLL//SFLif (!Q.empty()){if (gDist[v] > gDist[Q.front()]){Q.push_back(v);}else{Q.push_front(v);}}else{Q.push_back(v);}                //Q.push(v);if (v == t){isfind = true; break;}            }        }    }    if (gPre[t] == -1)  //若终点t没有设置pre,说明不存在到达终点t的路径        return false;    return true;}//加入边void InsertEdge(int u, int v, int vol, int cost){    gEdges[gEdgeCount].to = v;    gEdges[gEdgeCount].vol = vol;    gEdges[gEdgeCount].cost = cost;    gEdges[gEdgeCount].next = gHead[u];    gHead[u] = gEdgeCount++;}/////////////////////////////////////*最小费用最大流 end*//////////////////////////////////////////////////

0 0
原创粉丝点击