2016华为软件精英挑战赛 图论学习
- github
- 暴力深搜dfs
- 算法设计思想
- 算法分析
- 代码实现
- 高级用例贪心策略
- 算法设计思想
- 基于贪心思想的算法
- 算法的优化
- 算法分析
- 代码实现
- 算法设计思想
- 复赛心得
个人渣解,含有论文算法数据等,可进一步coding,欢迎star :)
github
赛题链接
从悄悄关注别人各种动态的过程中,知道了有一种神器叫“求解器”,原来其实就是调用库解决问题的方式!自己后来也想通过别人口中的求解器解决问题,但是发现基于求解器也是需要很强的建模能力的,,无论哪种解决问题的算法或者方法都需要一定能力!
在比赛途中耳濡目染,交流过程中,已知可解决NP问题的算法有
1),遗传算法
2),蛙跳算法
3),蒙特卡洛模拟随机算法,每次运行的结果都不会一样。
4),模拟退火
5),将问题转化成TSP旅行商问题,因为TSP问题和本问题很像,所以有人努力往此问题转化。
6),线性规划
暴力深搜dfs
算法设计思想
针对前五个低级案例的解决方案:
从起点开始深度优先搜索,并用数组记录沿途边号,当遇到终点时,此时就已经搜索到一条路径,否则就一直深度优先访问下去。
这条路径不一定全部经过毕竟点,所以要检查一下是否包含全部必经点。
如果全部包含必经点,则存储该路径,以后遇到比该路径权值更小的就更新路径。
显然这是一个递归的思路。
算法分析
这是一个暴力搜索,从起点到终点的路径随着顶点个数的增多有趋近指数级的条数,并且还要搜索出很多无效路径,所以相当费时。
代码实现:
注意:
- 1),采用邻接矩阵建图,矩阵中每个元素存储着边的索引号和权重
class Vertex //顶点的自定义(可能叫边的定义更好点) { public: Vertex() { LinkID = -1;//没有相连的索引号 Cost = MAX_WEIGHT; } int LinkID; int Cost; }; Vertex graph_matrix[MAX_VERTEX_NUMS][MAX_VERTEX_NUMS];//图数据,注意他是个全局变量
- 2)头文件中类的完整声明. 其中图是一个全局变量(邻接矩阵)!
#ifndef __ROUTE_H__ #define __ROUTE_H__ #include <stdio.h> #include <stdlib.h> #include <time.h> #include <map> #include <iostream> #include <string.h> #include <set> #include <stack> #include <queue> #include <vector> #include <functional> #include <time.h> using namespace std; void search_route(char *graph[5000], int edge_num, char *condition); ///updata 2016/04/03 /*以下为自定义函数****/ namespace cf { #define MAX_VERTEX_NUMS 600//最大顶点数目 #define MAX_WEIGHT -1//用-1代表最大边权值 #define MAX_MUST_PASS 50 class CodeCraft { public: CodeCraft(int edge_num){ m_vertexNums = 0; m_mustSize = 0; m_edgeNums = edge_num; m_pathSize = 0; m_minCost = 99999; m_countAns=0;//统计获得了多少条有效路径 flag_stop = false; isbadgraph = false; pMinPath = new int[MAX_VERTEX_NUMS]; for (int i = 0; i < MAX_MUST_PASS; i++) pMinPath[i] = 0; pMustPass = new int[MAX_MUST_PASS]; for (int i = 0; i < MAX_MUST_PASS; i++) pMustPass[i] = 0; }; ~CodeCraft(){ delete[] pMinPath; pMinPath = NULL; delete[] pMustPass; pMustPass = NULL; }; //建立图模型,邻接矩阵 void build_graph(char ** const topo, const int edge_num); void edge_to_num(char *one_edge, int *num); void demand_to_num(char * const demand); //两种策略 void senior_solution();//较大时的解决方案:寻找近似解 void primary_solution();//规模较小时的解决方案:寻找最优解 //核心函数:深度优先暴力穷举 void dfs_best_paths(int pre_cost, int start, int *visited); //核心函数:优化的暴力穷举 void force_valid_paths(int curStart, int *result, int idx, vector<int> visited_ban, int preCost); //核心函数的辅助函数 int next_adj_vertex(int curVex, int nextVex, bool flagfirst); int get_unvisited_size(vector<int> &visited_ban); void get_new_path(int *pResultPath, int path_size, int curCost); int dijkstra(int start_must, int *path, vector<int> &visited_ban, int *shortcost); //当前源点是否可达其他未访问的必经点,即shortcost中的最短路径值是有效的。 bool is_valid_connect(vector<int> &visited_ban, int *shortcost); void map_must_vertex(); int unconnected_size(); public: int m_startVex;//起点 int m_endVex;//终点 int m_vertexNums;//顶点个数, int m_edgeNums;//边的条数 int m_mustSize;//必经点个数 int m_pathSize;//结果路径的长度 int *pMinPath;//存储最小路径 int m_minCost;//最优路径的权值 int m_countAns;//统计获得了多少条有效路径 bool flag_stop;//终止标志 int *pMustPass;//存放必经点 map<int, int> mapping; bool isbadgraph; }; } #endif
- 3)深度优先暴力搜索满足条件的所有路径,结果只保存其中最短的一条
具体代码如下:
void CodeCraft::primary_solution()//规模较小时的解决方案:寻找最优解 { int visited[MAX_VERTEX_NUMS] = { 0 }; //已访问过的顶点 int cur_cost = 0; dfs_best_paths(cur_cost, this->m_startVex, visited);//开始递归寻找 //记录答案 for (int i = 0; i < this->m_pathSize; i++) record_result(pMinPath[i]); } //妈了个蛋,暴力出奇迹,暴力搜索执行函数 void CodeCraft::dfs_best_paths(int pre_cost, int start, int *visited) { int i = 0; static int path_size = 0; static int cur_cost = 0;//当前路径代价 static int path_count = 0;//当前符合要求的路径条数 static int path_stack[2000] = { 0 }; visited[start] = 1; if (start == this->m_endVex){//如果是终点 i = 0; bool flag = true; while (i < m_mustSize )//检查是否全部必经点都访问过 { if (visited[this->pMustPass[i]] != 1) { flag = false;//没有经过所有必经点 break; } i++; } if (flag && this->m_minCost > cur_cost)//必经点全部都访问过,并且当前路径权值更小,就记录路径 { this->get_new_path(path_stack, path_size, cur_cost);//更新最短路径 path_count++; } } else{ int nextVex = 0,curVex = start; bool flagfirst = true; while (nextVex != -1)//如果存在相邻点 { nextVex = next_adj_vertex(curVex, nextVex, flagfirst);//curVex相邻的下一个顶点 if (nextVex != -1 && visited[nextVex] == 0)//如果该相邻的顶点没有被访问过 { path_stack[path_size++] = graph_matrix[curVex][nextVex].LinkID; cur_cost += graph_matrix[curVex][nextVex].Cost; dfs_best_paths(graph_matrix[curVex][nextVex].Cost, nextVex, visited); } flagfirst = false; } } path_size--; cur_cost -= pre_cost; visited[start] = 0; } //在邻接矩阵从curVex行,第nextVex列开始找curVex的相邻点,即graph_matrix[curVex][i].Cost != -1的点 int CodeCraft::next_adj_vertex(int curVex, int nextVex, bool flagfirst) { int i = nextVex; if (!flagfirst) i++; for (; i < m_vertexNums; i++) { if (graph_matrix[curVex][i].Cost != -1) return i; } return -1; }
高级用例:贪心策略
算法设计思想:
实际上本算法是对期初算法思想的优化,主要的优化就在于我们不在盲目的寻找两个必经点的路径,而是总是寻找距离最近的必经点作为新起点去寻找下一个最近的必经点…….然而这样做还不够呢!
基于贪心思想的算法
在未把所有必经点访问完之前,终点是禁止访问的。
1)已起点作为“当前原点”,用Dijkstra算法将“当前原点”作为起点,找到起点距离其他未访问的必经点的距离,然后从小到大进行排序,显然我们应该总是寻找距离“当前原点”最近的必经点建立连接,显然选择比较远的必经点作为下一个“当前原点”作为起点很不明智。
2)如此往返,总是以“当前原点”最为起点寻找距离其他未被访问过的必经点的距离,下一个最短必经点最为新起点继续寻找下去
3)如果所有必经点都访问完了,接着就是连接终点的最后时刻!
a)当然前面的算法只是理想的状态,如果“当前原点”无法连接到未被访问的最短必经点呢?这显然有可能。当然,聪明的我们显然就就选择第二短的必经点作为连接点撒!
b)如果第二短,第三短…..均无法连接呢?(这显然也有可能)回退!回退到“当前原点(称呼他为,当前原点吧)”的上一个“当前原点(称呼他为,先前原点吧)”,此时不再选择这个不能建立连接的当前原点,转而去选择先前节点距离当前节点后面的第二短,第三…….短的必经点。
算法的优化
1)对Dijkstra算法的优化,因为整个算法的严重依赖该算法,他的算法时间复杂度严重影响找到有效路径的快慢!
2)如果找到一条有效路径后,我们将按照上述规则回退重新寻找路径,并记录最小权路径,如果在重新寻找路径的过程中(在完整的有效路径出来之前)发现大于了先前的有效路径,则直接回退,继续用回退法则寻找下一条有效路径!
3)如果“当前原点”无法到达余下未访问必经点,则按照回退原则进行回退处理!
算法分析
实践发现,即使总是在按照上述行为寻找必经点之间的最短路径,这种贪心搜索也很难找到最优解。
本质算该算法是一种贪心算法,实质上是优化的暴力搜索。在比赛中实际的操作方式就是限制程序运行时间为9800ms,如果算法没错,在这段时间内已经找到解,只是权值的很可能不是最优解。
代码实现:
github
个人渣解,含有论文算法数据等,可进一步coding,欢迎star :)
源码见下:
原文地址:http://blog.csdn.net/ebowtang/article/details/51220053
复赛心得
如果直接用初赛的方法分别找出两条路径P’和P’’的话,会有两个问题,一个是由于点数增多到了2000个会搜不出来,另一个是由于重合边数太多会难以出较优解。为了解决上述两个问题采用如下的思路:
(1) 使用DLX算法搜出两条路径的初始解。找一条路径的问题可以转化为01精确覆盖问题,只要把每条边选/不选作为一组01变量,每个点经过/不经过作为一组01变量,就能列出精确覆盖问题的方程。但传统的DLX模型解决此类图论问题的时候会产生问题,输出的路径可能含有环,因此DLX算法必须在递归的过程中实时进行去环操作,简单的说就是一旦发现成环立刻判为不合法解返回。
由于DLX算法会挑图中最薄弱的环节先进行搜索,因此搜索效率高,容易出可行解,搜索的每一步中还可以用Tarjan强连通缩点+Topo排序算法判断当前解是否一定是非法解进行剪枝。
(2) 使用迭代优化算法对初始解进行优化。因为初始解不保证解的优劣性,只保证可行性,因此可以将初始解按关键点分割成一段段“线路”,第一步优化是使用Dijkstra将每对相邻关键点之间的线路进行“绷紧”,用最短路替换DLX搜出来的弯弯曲曲的路径,并且通过增加重边权值有意回避另一条路径上的重边;第二步优化是交换关键点在路径上的出现次序,并且使用“绷紧”算法重新构造可行解,让解的总权值一步步变小。
总体来说,上述算法的问题在于DLX算法在深搜时仍不够强劲,大数据下可能会跑不出解;优化还不够彻底,比不上那些用线性规划算法跑出来的解优。但无论如何,至少这是一系列成功尝试,在写算法的过程中颇具收获。搭整个比赛算法框架的过程中,由于代码量大、细节多,因此对编程风格、DEBUG能力也是非常考验的。在快提交截止的时候发现最短路算法写挂了玩命DEBUG,是一种多么酸爽的体验……
注:本博文为EbowTang原创,后续可能继续更新本文。如果转载,请务必复制本条信息!
原文地址:http://blog.csdn.net/ebowtang/article/details/51220053
原作者博客:http://blog.csdn.net/ebowtang
2016华为软件精英挑战赛 图论学习相关推荐
- 2016华为软件精英挑战赛:赛题及其答疑汇总
注:本文文字均摘自官方指定网站和论坛,权威且可信,答疑见中间部分,非常全,众玩家可放心阅读. 同时文末给出了包括自己在内的诸多玩家的解法. 前言 赛题源自"未来网络"业务发放中的路 ...
- 2016华为软件精英挑战赛总结
还记得当时做这个比赛的时候好多天都是满脑子想着这道题,除了后面没有意义的玄学调参,参加这种比赛还是很锻炼的.在比赛的过程中需要去学算法读论文这都是一种提高吧.这里写篇文章分析一下当时的思路同时也纪念当 ...
- 【2023华为软件精英挑战赛暨HCSD校园沙龙】首场告捷!
摘要:近日,[2023华为软件精英挑战赛暨HCSD校园沙龙]活动在杭州电子科技大学首场告捷. 近日,[2023华为软件精英挑战赛暨HCSD校园沙龙]活动在杭州电子科技大学首场告捷.本次活动由华为技术有 ...
- 2023华为软件精英挑战赛,探寻软件人才与科技创新的最优解
作者 | 曾响铃 文 | 响铃说 今天,软件行业正呈现出江河入海一般的大汇流趋势. 一方面是技术的汇流,诸如人工智能等前沿技术与软件行业的深度融合,正全面颠覆软件产品的开发模式和服务逻辑. 另一方面则 ...
- 2017华为软件精英挑战赛小结
// 2017华为软件精英挑战赛小结 // 不说废话,直接上货!希望对目前的参赛者,或日后学习的人,提供一些参考和思路. #include <赛题说明.pdf> // 见附录文件 ...
- 2018华为软件精英挑战赛
今天12点,历时一个多月的2018华为软件精英挑战赛训练赛结束了,最后分数215.597(总分300),很遗憾,前64都没能进,不过还算尽力坚持到最后. 3月初,华为软赛开始一周后,看到师兄他们在弄, ...
- 2017华为软件精英挑战赛参赛过程回顾与心得
参赛队名:武长区 枪林弹雨 2017年4月26日,一波三折的复赛终于结束了,我们队最终没能进入决赛.虽然在意料之中,不过还是有些小失望.已经为这个比赛忙了一个月,突然之间不知道干什么好了,干脆写一写自 ...
- 2023第九届华为软件精英挑战赛启动报名,最高20万元奖金激励
2023年3月1日,第九届华为软件精英挑战赛正式启动报名,征召全球在校大学生前来应战.本届大赛由华为云承办,将围绕"普朗克计划"主题展开,华为资深专家技术团队出题,邀请全球高校软件 ...
- # 2021华为软件精英挑战赛C/C++——build.sh/build_and_run.sh/CodeCraft_zip.sh注释
2021华为软件精英挑战赛C/C++--build.sh/build_and_run.sh/CodeCraft_zip.sh注释 1.build.sh #!/bin/bashSCRIPT=$(read ...
最新文章
- 用 cooking 搭建一个简单又优雅的 Vue 项目开发环境 (入门篇)
- 配置Keil C51配置开发 STC51单片机过程
- discuz uc密码修改
- 基于 HTML5 网络拓扑图的快速开发之入门篇(二)
- unity 半透明混合问题_Unity Shader 透明度混合的双面渲染(十八)
- Python虚拟环境virtualenv的安装与使用详解(转)
- python3 从尾部读取_Python3基础:列表详解
- java.util.zip.ZipException: error in opening zip file
- 数据库的实现【笔记】
- MySQL Server Architecture
- html 图片墙效果,基于html5实现的图片墙效果
- 你看,Fastjson 漏洞也太多了吧。。
- 20200707每日一句
- 自定义系统右键菜单工具-使用说明
- 深度linux u盘安装教程,deepin 2014 u盘安装教程:u盘安装deepin2014步骤
- 深入浅出java web_深入浅出javaWeb实战第1讲Web的概念及其演变(上)
- Python爬取国家税务总局纳税信用A级纳税人信息!
- SpringBoot 无法捕获 maximum upload size exceeded
- Qt获取当前控件所在窗口的位置(坐标)
- 云计算360度 微软专家纵论产业变革
热门文章
- MQTT客户端工具介绍
- egg + nunjucks + sequelize初始化配置
- pytorch实现BN、LN、GN
- 计算机信息系统应急演练预案,网络信息系统应急预案
- ffmpeg rtmp 花屏_FFmpeg视频抽帧那些事
- Discuz 精心整理的搬家教程
- 守望者的逃离一道简单的DP
- iPhone苹果手机上一些不想让他人看到的APP应用图标怎么设置手机桌面上的APP应用设置隐藏不让显示在手机桌面隐藏后自己可以正常使用的方法?
- 科技向善•共同富裕,数字经济风口上的中国建筑供应链发展趋势
- android+自定义发彩信,Android实现获取短信验证码的功能以及自定义GUI短信验证