UVa1599 Ideal Path(双向bfs+字典序+非简单图的最短路+队列判重)

题目大意:

对于一个n个房间m条路径的迷宫(Labyrinth)(2<=n<=100000, 1<=m<=200000),每条路径上都涂有颜色,颜色取值范围为1<=c<=10^9。求从节点1到节点n的一条路径,使得经过的边尽量少,在这样的前提下,如果有多条路径边数均为最小,则颜色的字典序最小的路径获胜。一条路径可能连接两个相同的房间,一对房间之间可能有多条路径。输入保证可以从节点1到达节点n。

更多细节可以参考原题:UVa1599

分析:

  1. 从题目中我们可以看出,题目中的无向图是可以出现自环和重边的,自环我们可以在输入的时候检查并排除,但是重边我们需要保留,并从中选择颜色最小的边。
  2. 题目的数据量很大,不可能采用邻接矩阵存储图,因此应采用邻接表,且邻接表便于进行bfs
  3. 路径的颜色不代表路径的权重,本题中路径是无权的

思路:

从终点开始倒着bfs一次,得到每个点到终点的距离,然后从起点开始,按照每次距离减1的方法寻找接下来的点的编号。按照颜色最小的走,如果有多个颜色最小,则都拉入队列中,将最小的颜色记录在res数组中。

其中,index=d[0]-d[u]就得到了当前u节点对应的距离,也就是步骤数。

细节:

  1. 已经进入队列的节点不能重复入队,否则复杂度太高,会tle(重复入队的复杂度至少是O(n^2),在n=100000的情况下直接tle)
  2. 第一次bfs和第二次bfs的终止时机不同,第一次找到起点就终止,第二次则是从队列中取出节点时才能终止,为的是遍历完所有导向终点且路径长度一致的边,只有这样才能结果正确
  3. d数组记录每个节点到终点n的距离,不能用0进行初始化,而终点处的初始化必须是0
  4. d数组不能不初始化,否则对于多输入题目,前面的输入可能影响后面的输出

代码实现如下:

 1 #include<cstdio>
 2 #include<vector>
 3 #include<queue>
 4 #include<cstring>
 5 using namespace std; //min()函数
 6 #define max 100000
 7 #define inf 0x7fffffff
 8 typedef struct ver{
 9     int num, color;//边的另一端的节点编号 和 颜色
10     ver(int n,int c):num(n),color(c){}
11 }Ver;
12 int n,m,a,b,c;
13 int d[max],res[max];//d记录每个点到终点的最短距离 res记录最短路的颜色
14 bool vis[max],inqueue[max];//vis每个节点是否被访问过 inqueue标记节点是否加入了队列,防止重复加入
15 vector<Ver> edge[max];//邻接表记录图
16 void bfs(int start,int end){
17     memset(inqueue,0,n);
18     memset(vis,0,n);
19     int u,v,c;
20     queue<int> q;
21     q.push(start);
22     if(start==0){//用于正向bfs
23         memset(res,0,sizeof(int)*n);
24         while(!q.empty()){
25             u=q.front();q.pop();vis[u]=1;
26             if(u==n-1)return;
27             int minc=inf,len=edge[u].size();
28             for(int i=0;i<len;i++)if(!vis[v=edge[u][i].num] && d[u]-1==d[v])minc=min(edge[u][i].color,minc);//获取所有路径中最小的颜色
29             for(int i=0;i<len;i++)if(!vis[v=edge[u][i].num] && d[u]-1==d[v] && edge[u][i].color==minc && !inqueue[v])q.push(v),inqueue[v]=1; //若有多组颜色相同,且未入队,则将其入队
30             int index=d[0]-d[u];//获得当前步数对应的下标
31             if(res[index]==0)res[index]=minc;
32             else res[index]=min(res[index],minc);//获取最小颜色
33         }
34     }//用于反向bfs 构建层次图,找最短路
35     else while(!q.empty()){
36         u=q.front();q.pop();vis[u]=1;
37         for(int i=0,len=edge[u].size();i<len;i++)if(!vis[v=edge[u][i].num] && !inqueue[v]){
38             d[v]=d[u]+1; //一定是头一次入队,这通过inqueue保证
39             if(v==0)return; //找到起点,退出
40             q.push(v);//如果不是起点,就把这个点入队
41             inqueue[v]=1;//入队标记
42         }
43     }
44 }
45 int main(){
46     while(scanf("%d%d",&n,&m)==2){
47         for(int i=0;i<n;i++)edge[i].clear();
48         memset(d,-1,sizeof(int)*n);d[n-1]=0;//注意初始化的细节
49         while(m--){
50             scanf("%d%d%d",&a,&b,&c);
51             if(a!=b){ //排除自环
52                 edge[a-1].push_back(ver(b-1,c));
53                 edge[b-1].push_back(ver(a-1,c));
54             }
55         }
56         bfs(n-1,0);//反向bfs
57         bfs(0,n-1);//正向bfs
58         printf("%d\n%d",d[0],res[0]);
59         for(int i=1;i<d[0];i++)printf(" %d",res[i]);
60         printf("\n");
61     }
62 }

收获:

这是第一次学习bfs遍历复杂图,对于重边和自环的处理也终于有了一点经验,积累了自己的bfs最短路的模板

另外,UVa上的数据并不是完全可靠,对于用0初始化数组d的行为,可以用这组数据测试出wa的结果:

Input:

4 3

1 2 1

1 3 1

1 4 7

Output:

1

7

但是我实验发现,如果用0对数组d进行初始化,在UVa上仍能AC,不过我已经给UVa写信报告了这个bug,不知道他们会不会做修正。

不论如何,这道题还是收获很大滴~接下来是

反向bfs寻找最短路的模板

注意:d数组初始化应该用-1,并将d[n-1]=0,否则就会出现上述UVa的bug

这份代码假设在输入的时候重边已经被排除,否则这份代码还需要加入u!=v的判断

代码如下:

 1 void rbfs(){
 2     memset(inqueue,0,sizeof(inqueue));
 3     memset(vis,0,sizeof(vis));
 4     queue<int> q;q.push(n-1);
 5     while(!q.empty()){
 6         u=q.front();q.pop();vis[u]=1;
 7         for(int i=0,len=edge[u].size();i<len;i++)if(!vis[v=edge[u][i].num] && !inqueue[v]){ //inqueue是为了防止重复入队造成复杂度过高,以本题为例,如果允许重复入队会直接超时
 8             d[v]=d[u]+1;
 9             if(v==0)return; //找到起点,退出
10             q.push(v);//如果不是起点,就把这个点入队
11             inqueue[v]=1;//入队标记
12         }
13     }
14 }

今天就到这里啦~再见呦(●‘?‘●)~~撒花撒花*★,°*:.☆\( ̄▽ ̄)/$:*.°★*

时间: 2024-10-06 19:54:54

UVa1599 Ideal Path(双向bfs+字典序+非简单图的最短路+队列判重)的相关文章

UVA 1599 Ideal Path(双向bfs+字典序+非简单图的最短路+队列判重)

https://vjudge.net/problem/UVA-1599 给一个n个点m条边(2<=n<=100000,1<=m<=200000)的无向图,每条边上都涂有一种颜色.求从结点1到结点n的一条路径,使得经过的边数尽量少,在此前提下,经过边的颜色序列的字典序最小.一对结点可能有多条边,一条边可能连接相同的结点(自环).输入保证结点1可以到达结点n.颜色是1~10^9的整数. 分析: 从题目中我们可以看出,题目中的无向图是可以出现自环和重边的,自环我们可以在输入的时候检查并排

UVa1599,Ideal Path

说实话,这题参考的: http://blog.csdn.net/u013382399/article/details/38227917 倒着BFS就把我难住了T T,原来这样倒着BFS一遍,遍历完所有的点后就能得到每一点到终点的最短距离啊(其实做完反思后仔细想了想,发现其实当第一次bfs到首节点时,该图已经遍历完成了),一开始没转过这个弯来T T,我直接调用了N次dfs(i)囧rz.... 一开始我还想着吧color[]排序...这样更费时.....我写的dfs2()写着写着就乱了,本来思路还清

UVa 1599 Ideal Path【BFS】

题意:给出n个点,m条边,每条边上涂有一个颜色,求从节点1到节点n的最短路径,如果最短路径有多条,要求经过的边上的颜色的字典序最小 紫书的思路:第一次从终点bfs,求出各个节点到终点的最短距离, 第二次bfs从起点沿着每到达一个节点d[]减少1来走,按照颜色的字典序最小的路径来走 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include <cmath> 5 #include&

POJ3967Ideal Path[反向bfs 层次图]

Ideal Path Time Limit: 10000MS   Memory Limit: 65536K Total Submissions: 1754   Accepted: 240 Description New labyrinth attraction is open in New Lostland amusement park. The labyrinth consists of n rooms connected by m passages. Each passage is colo

UVA-1604 Cubic Eight-Puzzle (双向BFS+状态压缩+限制搜索层数)

题目大意:立体的八数码问题,一次操作是滚动一次方块,问从初始状态到目标状态的最少滚动次数. 题目分析:这道题已知初始状态和目标状态,且又状态数目庞大,适宜用双向BFS.每个小方块有6种状态,整个大方格有9*6^8个状态.每个小方块用一位6进制数表示即可. 注意:状态转移时要谨慎,否则会出现意想不到的错误: 这道题的末状态有256(2^8)个,如果对搜索层数不加限制,即使双向BFS也会TLE的,当限制正向搜索15层逆向搜索15层至正向搜索27层反向搜索3层时都能AC(我下面贴出的程序是这样的),其

poj 1077 Eight(双向bfs)

题目链接:http://poj.org/problem?id=1077 思路分析:题目要求在找出最短的移动路径,使得从给定的状态到达最终状态. <1>搜索算法选择:由于需要找出最短的移动路径,所以选择bfs搜索 <2>判重方法: 将空格视为数字9,则可以将状态的集合视为1-9的排列组合的集合,根据康托展开,将每一个状态映射到一个正整数中: 在由哈希表进行判重. <3>状态压缩:在搜索时使用将每个状态转换为一个由1-9组成的9位数字即可进行状态压缩与解压. <4&g

第六章部分例题 双向bfs邻接表和邻接矩阵实现

Idealpath 双向bfs输出颜色,邻接矩阵实现 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <queue> 5 #include <map> 6 #include <algorithm> 7 8 using namespace std; 9 10 const int maxn=10000; 11 const int inf=1

Colour Hash (Uva 704 双向bfs)

Colour Hash Time Limit: 3000MS   Memory Limit: Unknown   64bit IO Format: %lld & %llu Submit Status Description This puzzle consists of two wheels. Both wheels can rotate both clock and counter-clockwise. They contain 21 coloured pieces, 10 of which

双向BFS(转)

(转) 双向BFS(http://www.cppblog.com/Yuan/archive/2011/02/23/140553.aspx) 如果目标也已知的话,用双向BFS能很大提高速度 单向时,是 b^len的扩展. 双向的话,2*b^(len/2)  快了很多,特别是分支因子b较大时 至于实现上,网上有些做法是用两个队列,交替节点搜索 ×,如下面的伪代码:    while(!empty()){ 扩展正向一个节点 遇到反向已经扩展的return 扩展反向一个节点 遇到正向已经扩展的retur