初学算法之uva1599 IdealPath

题目:给一个n个点和m条边的无向图,每条边都涂有一种颜色。求从结点1到结点n的一条路径,使得经过的边数尽量少,在此前提下,经过边的颜色序列的字典序最小。一对结点间可能有多条边,一条边可能连接两个相同结点。输入保证结点1可以达到结点n。颜色1~1000000000(不用数了,九个零,忘记怎么弄上标了,也懒得去查)。

思路:这道题目需要采用正反两次bfs,先是反着用一次,求得每个结点到终点的路径,再具体一点就是求得结点1到所求结点的最短路径。然后再正着开始走,看是每次到达一个新结点时要保证d值恰好减一,直到到达终点。这里我分析一下为什么不能用一次bfs:我们假设用一次,首先从结点1出发,但是这个时候就不知道选择的最小权值的那个路径能不能到达。如图所示,如果选择结点5,则永远不能到达最终结点8.但是反向bfs后,就不能选择结点5了,因为,结点5比结点2到结点8的距离更远。这下就应该明白了。

talk is cheap ,show me your code!

 1 #include<cstdio>
 2 #include<vector>
 3 #include<queue>
 4 #include<cstring>
 5 using namespace std; //min()函数
 6 #define max 100000
 7 #define inf 10000000+10
 8 bool vis[max],in_q[max];
 9 int m,n,a,b,c;//边数、结点数、结点、下一个结点、颜色
10 int d[max];//记录各节点到终点的路径长度
11 int res[max];//记录最短路的权值
12
13 typedef struct edge {
14     int pot, color;
15     edge(int v = 0, int c = 0) :pot(v), color(c){}
16 }Edge;//还有这种操作,我也是第一次用,赋值很方便
17
18 vector<Edge>map[max];//记录邻接表
19 void re_bfs() {
20     int u, v;
21     memset(vis,0,sizeof(vis));
22     memset(in_q, 0, sizeof(in_q));
23     queue<int>q;
24     q.push(n - 1);
25     vis[n - 1] = true;
26     d[n - 1] = 0;
27 //在map里面存在着所有的节点以及他们之间的连接关系。map【u】.size代表着节点u相连节点的个数;
28     while (!q.empty()) {
29         u = q.front(); q.pop(); vis[u] = true;
30         for (int i = 0; i < map[u].size(); i++) {
31             v = map[u][i].pot;
32             if (!vis[v]&&!in_q[v]) {
33                 vis[v] = true;
34                 d[v] = d[u] + 1;
35                 q.push(v);
36                 in_q[v] = true;
37             }
38         }
39     }
40 }
41
42 void bfs() {
43     memset(in_q, 0, n);
44     memset(vis, 0, sizeof(vis));
45     int u, v;
46     queue<int>q;
47     q.push(0);
48     memset(res, 0, n*sizeof(int));//res这个数组,存储了每个最短路径里面每条边的权值
49     while (!q.empty()) {
50         u = q.front(); q.pop(); vis[u] = true;
51         int minc = inf, num = map[u].size();
52         for (int i = 0; i < num; i++) {//这个循环是找出与节点u相连的边的最小权值
53             v = map[u][i].pot;
54             if (!vis[v] && d[u] - 1 == d[v])
55                 minc = min(map[u][i].color, minc);
56         }
57         for (int i = 0; i < num; i++) {//最小权值的边不一定是一个,所以把所对应的另一个结点压入队列
58             v = map[u][i].pot;
59             if (!vis[v] && d[u] - 1 == d[v] && map[u][i].color == minc && !in_q[v])
60                 q.push(v); in_q[v] = true;
61         }
62         int index = d[0] - d[u];//这里就有学问了,因为就不仅要求得最短的路径,还要满足这些边是相连的
63         if (res[index] == 0)res[index] = minc;
64         else {
65             if (minc > res[index]) {//这里把前一段权值相同的,而压入队列的几个结点pop出去较大的,留下最下的,如果实在不懂,就把(121)(131)(243)(352)(464)(565)画一个图就明白了。
66                 int temp_minc = q.front(); q.pop(); q.pop(); q.push(temp_minc);
67             }
68             else if (minc < res[index])q.pop();
69         }
70     }
71 }
72
73 int main() {
74     while (scanf_s("%d%d", &n, &m) == 2) {
75         for (int j = 0; j < n; j++)map[j].clear();
76         memset(d, -1, sizeof(int)*n);
77         while (m--) {
78             scanf_s("%d%d%d", &a, &b, &c);
79             if (a != b) {//我觉得in_q的判断已经去除了自环和重边的问题
80                 map[a - 1].push_back(edge(b - 1, c));
81                 map[b - 1].push_back(edge(a - 1, c));
82             }
83         }
84         re_bfs();
85         bfs();
86         printf("%d\n%d", d[0], res[0]);
87         for (int i = 1; i < d[0]; i++)printf("%3d", res[i]);
88         printf("\n");
89     }
90 }

总结:第一次对图进行bfs操作,其中的困难也是可以想象的,但是念念不忘,必有回响。这次确实收获很多,更加熟悉使用bfs,对图的理解等等,这一切都是建立在实际操作的基础上,所以看完还不自己码一遍吗?

时间: 2024-10-20 06:39:04

初学算法之uva1599 IdealPath的相关文章

初学算法-快速排序与线性时间选择(Deterministic Selection)的C++实现

快速排序算法其实只做了两件事:寻找分割点(pivot)和交换数据. 所谓寻找分割点,既找到一个预计会在中间位置附近的点,当然尽量越接近中点越好. 所谓交换数据,就是把比这个分割点小的数据,最终都放在分割点左边,比它大的都放在右边. 设要排序的数组是A[left]--A[right],首先任意选取一个数据(一般算法:使用随机数选取一个区间内的数. 文艺算法:取A[left].A[right]和A[rand()]的中值. 二笔算法:选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它前面,

初学算法-分治法求平面上最近点对(Closest Pair)-HDU 1007

本来这个算法在笔者电脑里无人问津过一段时间了,但今天正好做HDU 1007见到了这个问题,今天就来把代码分享出来吧! 我们首先将所有点按照坐标x排序一下,再做一条直线l当作"分割线",方便我们递归. 然后,我们就可以把这些点按照x轴的坐标分为左半部分和右半部分.那么最短距离一定在左半部分.右半部分.跨越左右的点对中的一个. 那么你可能会有疑问了:本来最近点对也一定在这三个区域内,这不还是相当于什么都没干吗? 还真不是.我们可以假设通过递归得到了左边最小距离为d1,右边最小距离为d2,令

初学算法 - 求凸包的Garham‘s Scan算法的C++实现

所谓凸包,就是一个计算几何(图形学)中的概念.用不严谨的话来讲,给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边型,它能包含点集中所有的点.维基百科对集合X的凸包(Convex Hull)有四个定义,分别为: The (unique) minimal convex set containing X            ---  包含集合X的最小凸集合 The intersection of all convex sets containing X          --- 所有包

【插入排序算法】初学算法之排序--直接插入排序

前言: 厚厚一本<算法第四版>,看到五分之一就已经收益良多,而前五分之一又大部分是关于排序,有冒泡排序.快速排序.堆排序.直接插入排序.希尔排序等等,理解起来也不算特别的难,今天就跟大家分享其中的一种 —— 直接插入排序算法,这里我实现了javascript和java两个语言版本. 思路: 在生活中,如果我们要对扑克牌按大小排序,我们会怎么排呢? ① 首先找出一张牌 放在桌子上 ② 拿出第二张牌,比第一张小就放上面,比第一张大就放下面 ③ 拿出第三张牌,比第一张小就放上面,比第一张大就和第二张

初学算法-基于最小堆的优先级队列C++实现

笔者近日实现了最小堆类及其派生的优先级队列,特将代码奉上,不足之处还请指出! 在实现优先级队列时,笔者表示萌萌哒没有用过template写派生类,结果写完了出现error: *** was not decleared in this scope..后来各种补上this->才完事,在CSDN(笔者的帖子地址? http://bbs.csdn.net/topics/391806995)上提问后才知道是模板参数依赖,笔者表示涨姿势了.. /**  * The Minimum Heap Class an

初学算法之最基础的欧拉回路

须知: 图中的度:所谓顶点的度(degree),就是指和该顶点相关联的边数. 在有向图中,度又分为入度和出度. 入度 (in-degree) :以某顶点为弧头,终止于该顶点的弧的数目称为该顶点的入度. 出度 (out-degree) 是指以某顶点为弧尾,起始于该顶点的弧的数目. 在某顶点的入度和出度的和称为该顶点的度 定义: 欧拉回路:每条边恰好只走一次,并能回到出发点的路径 欧拉路径:经过每一条边一次,但是不要求回到起始点 欧拉回路存在性的判定: 一.无向图每个顶点的度数都是偶数,则存在欧拉回

初学算法之(二叉)堆

堆最重要的性质就是儿子的值一定不小于父亲的值. 堆的push与pop操作的时间复杂度都是O(logn) 堆也是二叉树,所以满足: 左儿子的编号是自己编号*2+1: 右儿子的编号是自己编号*2+2: (左儿子与右儿子没有对应的大小关系) 堆还是很好理解的,能理解树就能理解堆. 附代码: 1 int heap[Max],sz=0; 2 void pish(int x){ 3 //自己的节点 4 int i=sz++; 5 while(i>0){ 6 int p=(i+1)/2;//父亲的节点 7 i

如何拿到阿里算法校招offer

好多同学有问过怎么能拿到阿里算法类校招的offer,刚好看到这篇文章分享给大家,详情可以看原文链接,原文链接中有视频讲解. 师兄师姐的建议: 之前初学算法的时候上过的公开课和看过的书 1. Coursera:<Machine Learning>.<Pattern Discovery in Data Mining>.<R Programming>,平台特点:通俗易懂,适合入门,看完公开课基本能写出作业 2. edx:<Introduction to Big Data

快速排序算法学习总结

排序算法有很多种,如冒泡排序.堆排序.快速排序等: 冒泡排序是大学的初学算法,也被很多人所熟知,这里不详细讨论,实现方式如下: 快速排序使用分治法,将一个list分为两个子list: 算法步骤:1 从数列中挑出一个元素,作为基准,2 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边).在这个分区退出之后,该基准就处于数列的中间位置.这个称为分区操作.3 递归地把小于基准值元素的子数列和大于基准值元素的子数列排序.递归的最底部情形,是数列的