2016华为精英挑战赛初赛题目解析

一、题目定义:

给定一个带权重的有向图 G=(V,E), V 为顶点集, E 为有向边集,每一条有向边均有一个权重。对于给定的顶点 s、 t,以及 V 的子集 V',寻找从 s 到 t 的不成环有向路径 P,使得 P经过 V'中所有的顶点(对经过 V'中节点的顺序不做要求)。
若不存在这样的有向路径 P,则输出无解,程序运行时间越短,则视为结果越优;
若存在这样的有向路径 P,则输出所得到的路径,路径的权重越小,则视为结果越优,在输出路径权重一样的前提下,程序运行时间越短,则视为结果越优。
说明:
1) 图中所有权重均为[1, 20]内的整数;(保证权值都是正值)
2) 任一有向边的起点不等于终点;(不构成环)
3) 连接顶点 A 至顶点 B 的有向边可能超过一条,其权重可能一样,也可能不一样;
4) 该有向图的顶点不会超过 600 个,每个顶点出度(以该点为起点的有向边的数量)不超过8;
5) V'中元素个数不超过 50;
6) 从 s 到 t 的不成环有向路径 P 是指, P 为由一系列有向边组成的从 s 至 t 的有向连通路径,且不允许重复经过任一节点;
7) 路径的权重是指所有组成该路径的所有有向边的权重之和。

参考:

2016华为Code Craft比赛必经点集寻径问题 67分算法简介

二、简单用例的解法

非递归深度优先路径搜索DFS

1. 深度优先遍历的基本实现思想:

(1) 访问顶点v;
(2) 从顶点v未被访问的邻接点中选取一个顶点w,再从顶点w出发继续进行深度优先遍历;
(3) 重复上述两步,直到图中所有和顶点v有路径相通的顶点都被访问到。

2. 非递归的伪代码实现:
栈S初始化;
visited[n] = 0;//初始化
访问顶点v;
visited[v] = 1;
顶点v入栈S;
while(栈S非空)
    x = 栈S的栈顶元素;//不出栈
    if(顶点x的邻接点w存在 && w未被访问)
        访问w;
        visited[w] = 1;
        w入栈S;
    else
        x出栈

注:但是根据题目要求,最短路径要包含所有必经点,因此在DFS算法的基础上增加2个剪枝条件:
(1) 当搜索的点为终点时,停止搜索,判断当前的搜索路径是否包含所有必经点,如果已经包含,再判断当前路径是否比已知路径短,记录下最短路径并回溯;
(2) 如果当前搜索路径的权重已经比记录的最短路径的权重要大时,停止搜索并回溯。

3. 举例说明:
2016华为精英挑战赛初赛题目解析_第1张图片
有向图.png
搜索过程.png

三、中高级用例的解法

基于贪心的分段路径Dijsktra算法

1. Dijsktra算法描述:

设G=(V,E)是一个带权有向图,把图中顶点集合V分成两组,
第一组为已求出最短路径的顶点集合,用S表示,初始时S中只有一个源点v,以后每求得一条从源点v到某点的最短路径 , 就将该点加入到集合S中,直到全部顶点都加入到S中,算法结束;
第二组为其余未确定最短路径的顶点集合,用U表示,按最短路径长度的递增次序依次把集合U中的顶点加入S中。在加入的过程中,始终保持从源点v到S中各顶点的最短路径长度不大于从源点v到U中任何顶点的最短路径长度。
参考:

透彻理解迪杰斯特拉算法

2. Dijsktra算法举例:
2016华为精英挑战赛初赛题目解析_第2张图片
有向图.png
2016华为精英挑战赛初赛题目解析_第3张图片
Dijsktra算法步骤.jpg
3. 基于贪心的分段路径Dijsktra算法实现思路:

从以上例子中可以看出,Dijsktra算法可以很好地解决从A到各点的(包括F点)的最短路径问题,但是却没有解决必经点问题,因此使用分段路径Dijsktra算法:
必经点:C,D,G
(1) 计算从A点到各个必经点的Dijsktra,得到一个最短路径集合:
A->B->C=14;
A->B->D=18;
A->B->C->G=23;
(2) 从以上集合中选择某条路径,假设选择A->B->C->G=23;(已经包含了2个必经点)
(3) 从图中移除该路径中的所有点;(我个人理解是从图中移除该路径中除终点以外的点)
(4) 从终点G开始,计算从G到剩余必经点(D点)的最短路径:
G->E->D=9;
(5) 再从图中移除以上路径中除终点D以外的点;
(6) 从终点D开始,计算D到最终点F的最短路径:
D->F=7;
(7) 将每一步选择的路径首尾相连:
A->B->C->G->E->D->F=39;
包含全部必经点,算法结束。

你可能感兴趣的:(2016华为精英挑战赛初赛题目解析)