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 40005
struct 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 MYDEBUGcout << "普通网络节点的边初始化:" << 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 MYDEBUGcout << 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 MYDEBUGcout << 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 MYDEBUGcout << 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 << "超级起点节点的边初始化:" << endl;
#endiffor(unsigned int i=0; i<server_pos.size(); i++){int temp_end = server_pos[i];
#ifdef MYDEBUGcout << start << "======>" << temp_end << " , " << needed_flow << " , " << 0 << endl;edge_count++;
#endifInsertEdge(start, temp_end, MAXINT, 0);}//加入超级终点节点的边
#ifdef MYDEBUGcout << "超级终点节点的边初始化:" << 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 MYDEBUGcout << temp_start << "======>" << end << " , " << bandwidth << " , " << 0 << endl;edge_count++;
#endifInsertEdge(temp_start, end, bandwidth, 0);}
#ifdef MYDEBUGcout << "加入的总边数:" << 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*//

2017华为软挑——最小费用最大流(MCMF)相关推荐

  1. 最小费用最大流MCMF算法

    摘要 在复杂网络研究中,单源单点的最小费用最大流算法(以下简称MCMF)的应用十分广泛,也引申出类似预流推进.ZKW.SPFA等相关方法. 在华为2017CodeCraft中,MCMF可以说是各家实力 ...

  2. HDU 6445 Search for Answer(最小费用最大流-mcmf)

    Description 给出一个nnn个点的完全图的邻接矩阵aaa,其中ai,j=1a_{i,j}=1ai,j​=1表示i,ji,ji,j之间边的方向是iii到jjj,ai,j=0a_{i,j}=0a ...

  3. HDU5619 Jam's store(最小费用最大流 MCMF)

    题意 n个顾客m个服务员,给出每个服务员给每个顾客服务需要的时间,求顾客最小的等待时间 建图 网络流真是玄学啊,就是不会建图.. 源点向每个客户连边,控制流量为n 服务员拆点,每个客户向每个服务员连n ...

  4. 最小费用最大流 【模板】

    如果理解了最大流连续增广路算法的思维, 理解这个算法还是很简单的. 结构体存储信息: 分别为边的起点.终点.容量.当前流量.费用.下一条边的编号. struct Edge {int from, to, ...

  5. 2022华为软挑比赛(初赛笔记)

    文章目录 2022华为软挑(初赛笔记) 1. 赛题要求 2. 解决方案 2.1 挑选适合的边缘节点 2.2 第一轮:最大分配 2.3 第二轮:均值分配 总结 本文仓库地址: Github-CodeCr ...

  6. 2020华为软挑热身赛代码开源-思路大起底(华为软件精英挑战赛编程闯关)

    本文首发于个人公众号[两猿社],后台回复[华为],获取完整开源代码链接. 昵称:lou_shang_shi_bian_tai 成绩:0.032 社长没有针对硬件做任何优化,热身赛成绩也一般.但有些比赛 ...

  7. O准备如何苟进复赛圈?华为软挑开挂指南(附赛题预测)

    这篇文章纯粹是心血来潮,原因是去年上传到github的参赛代码,前几天又有两个人star和fork了. 记得star热潮还是去年4月复赛刚结束的那几天,厚着脸皮在八大赛区的群里发消息求关注,之后就陷入 ...

  8. 华为软挑赛2023-复赛笔记

    前言 比赛介绍 参考初赛博客: 华为软挑赛2023-初赛笔记_没有梦想的大白兔的博客-CSDN博客 赛题变动 官网赛题: 华为云论坛_云计算论坛_开发者论坛_技术论坛-华为云 (huaweicloud ...

  9. 乌鲁木齐网络赛J题(最小费用最大流模板)

    ACM ICPC 乌鲁木齐网络赛 J. Our Journey of Dalian Ends 2017-09-09 17:24 243人阅读 评论(0) 收藏 举报  分类: 网络流(33)  版权声 ...

  10. POJ - 2516 Minimum Cost 最小费用最大流

    题目链接 题意:给n,m,k表示商店数,储存店数,种类数 然后给n*k表示每个水果店需求每种种类的数量: 表示成 need[i][j] 再给m*k表示每个储存店每种种类数量: 表示成store[i][ ...

最新文章

  1. java 定时删除_Java编写定时删除文件程序
  2. [TCP/IP] TCP在listen时的参数backlog的意义
  3. 2010 本年度认证目标:坐沙发的熊
  4. pat00-自测5. Shuffling Machine (20)
  5. 回归分析什么时候取对数_冬蜜什么时候取,冬天取蜂蜜的方法
  6. (计算机组成原理)第三章存储系统-第七节2:页式/段式虚拟存储器
  7. 2-1:配置与环境之环境
  8. PHP包含文件函数include、include_once、require、require_once区别和总结
  9. java三种功能加强模式
  10. Python defaultdict() 的理解
  11. Windows 32位下cocos2d-x2.2.0Android环境搭建
  12. 感恩节,《2012》,尖叫
  13. Java中输出所有的水仙花数
  14. fir.im关于iOS Ad hoc内测版下载详细流程
  15. linux安装福昕PDF阅读器
  16. 台达内部速度指令_急等:Smart200控制台达B2伺服,原点手动速度错误?GOTO暂停、停止设计?暂停再继续动作不对?-工业支持中心-西门子中国...
  17. 【文献管理】Zotero插件QuickLook || 让Zotero具备文献预览功能
  18. <FreeRTOS入门第九节>事件标志位
  19. 过孔盖油与过孔开窗的区别
  20. thymeleaf 使用 符号问题

热门文章

  1. 计算机考试画箭头,几何画板如何画箭头 绘制方法介绍
  2. java虚拟机执行过程
  3. IIS——asp上传文件大小限制
  4. catia飞机建模+flightgear飞行仿真
  5. linux 下的绘图软件Visio
  6. 04-新拟物单选按钮样式
  7. linux打开mid格式音乐,mid文件扩展名,mid文件怎么打开?
  8. html5 video标签嵌入视频
  9. 计算机组成与结构 第四版pdf,计算机组成与体系结构(原书第4版)
  10. 实现1V1音视频实时互动直播系统 十二、第一节 STUN_TURN服务器搭建