题目大意:

对于一个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数组不能不初始化,否则对于多输入题目,前面的输入可能影响后面的输出

代码实现如下:

 #include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
using namespace std; //min()函数
#define max 100000
#define inf 0x7fffffff
typedef struct ver{
int num, color;//边的另一端的节点编号 和 颜色
ver(int n,int c):num(n),color(c){}
}Ver;
int n,m,a,b,c;
int d[max],res[max];//d记录每个点到终点的最短距离 res记录最短路的颜色
bool vis[max],inqueue[max];//vis每个节点是否被访问过 inqueue标记节点是否加入了队列,防止重复加入
vector<Ver> edge[max];//邻接表记录图
void bfs(int start,int end){
memset(inqueue,,n);
memset(vis,,n);
int u,v,c;
queue<int> q;
q.push(start);
if(start==){//用于正向bfs
memset(res,,sizeof(int)*n);
while(!q.empty()){
u=q.front();q.pop();vis[u]=;
if(u==n-)return;
int minc=inf,len=edge[u].size();
for(int i=;i<len;i++)if(!vis[v=edge[u][i].num] && d[u]-==d[v])minc=min(edge[u][i].color,minc);//获取所有路径中最小的颜色
for(int i=;i<len;i++)if(!vis[v=edge[u][i].num] && d[u]-==d[v] && edge[u][i].color==minc && !inqueue[v])q.push(v),inqueue[v]=; //若有多组颜色相同,且未入队,则将其入队
int index=d[]-d[u];//获得当前步数对应的下标
if(res[index]==)res[index]=minc;
else res[index]=min(res[index],minc);//获取最小颜色
}
}//用于反向bfs 构建层次图,找最短路
else while(!q.empty()){
u=q.front();q.pop();vis[u]=;
for(int i=,len=edge[u].size();i<len;i++)if(!vis[v=edge[u][i].num] && !inqueue[v]){
d[v]=d[u]+; //一定是头一次入队,这通过inqueue保证
if(v==)return; //找到起点,退出
q.push(v);//如果不是起点,就把这个点入队
inqueue[v]=;//入队标记
}
}
}
int main(){
while(scanf("%d%d",&n,&m)==){
for(int i=;i<n;i++)edge[i].clear();
memset(d,-,sizeof(int)*n);d[n-]=;//注意初始化的细节
while(m--){
scanf("%d%d%d",&a,&b,&c);
if(a!=b){ //排除自环
edge[a-].push_back(ver(b-,c));
edge[b-].push_back(ver(a-,c));
}
}
bfs(n-,);//反向bfs
bfs(,n-);//正向bfs
printf("%d\n%d",d[],res[]);
for(int i=;i<d[];i++)printf(" %d",res[i]);
printf("\n");
}
}

收获:

这是第一次学习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的判断

代码如下:

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

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

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

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

    https://vjudge.net/problem/UVA-1599 给一个n个点m条边(2<=n<=100000,1<=m<=200000)的无向图,每条边上都涂有一种颜色 ...

  2. Uva 1599 Ideal Path - 双向BFS

    题目连接和描述以后再补 这题思路很简单但还真没少折腾,前后修改提交了七八次才AC...(也说明自己有多菜了).. 注意问题: 1.看清楚原题的输入输出要求,刚了书上的中文题目直接开撸,以为输入输出都是 ...

  3. UVA-1599 Ideal Path(双向BFS)

    题目: 给一个n个点m条边(2≤m≤100000, 1≤m≤200000)的无向图,每条边上都涂有一种颜色(用1到1000000000表示).求从结点1到结点n的一条路径, 使得经过的边数尽量少,在此 ...

  4. UVa 1599 Ideal Path【BFS】

    题意:给出n个点,m条边,每条边上涂有一个颜色,求从节点1到节点n的最短路径,如果最短路径有多条,要求经过的边上的颜色的字典序最小 紫书的思路:第一次从终点bfs,求出各个节点到终点的最短距离, 第二 ...

  5. UVa1599,Ideal Path

      说实话,这题参考的: http://blog.csdn.net/u013382399/article/details/38227917 倒着BFS就把我难住了T T,原来这样倒着BFS一遍,遍历完 ...

  6. UVA1601-The Morning after Halloween(双向BFS)

    Problem UVA1601-The Morning after Halloween Accept: 289 Submit: 3136 Time Limit: 12000 mSec  Problem ...

  7. 习题:八数码难题(双向BFS)

    八数码难题(wikioi1225) [题目描述] 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要求解的问题是:给出 ...

  8. ACM-BFS之Open the Lock——hdu1195(双向BFS)

    这道题的0基础版本号,暴力BFS及题目详情请戳:http://blog.csdn.net/lttree/article/details/24658031 上回书说道,要用双向BFS来尝试一下. 最终A ...

  9. luoguP1379-八数码难题(双向bfs)

    题目链接:https://www.luogu.org/problemnew/show/P1379 题意:用字符串表示八数码,求根据给定八数码得到末状态“123804765”最少的步数. 思路:这题很方 ...

随机推荐

  1. Xcode 6.0中彻底关闭ARC

    对整个项目关闭ARCproject -> Build settings -> Apple LLVM complier 3.0 - Language -> objective-C Au ...

  2. BZOJ3573 [Hnoi2014]米特运输 【贪心】

    题目链接 BZOJ3573 题解 题目又臭又长系列 题意:修改尽量少的点权,使得: ①同个节点的所有儿子点权相同 ②任意非叶节点权值等于其儿子权值之和 容易发现一旦任意一个点权值确定,整棵树权值就确定 ...

  3. git使用笔记(九)操作原理

    By francis_hao    Nov 27,2016   参考[1]的一张图已经把git的基本原理描述的很清楚了,如下:   下面以实例演示其过程,需要用到两个命令cat-file和ls-fil ...

  4. ubuntu使用su切换root用户提示“认证失败”

    在虚拟机上安装了ubuntu,安装时提示设置密码,也设置了,但是在终端操作时,遇到权限不够的问题,于是就想到就是要切换root用户,获取最高权限. 当我使用 su 切换到root用户时,提示我输入密码 ...

  5. LwIP - 打开keepalive功能

    在服务器端打开keepalive功能 1.保证LWIP_TCP_KEEPALIVE被定义为1,(这样TCP_KEEPIDLE.TCP_KEEPINTVL和TCP_KEEPCNT 设置才有效) 2. i ...

  6. 编程技巧 - malloc()与free()

    1.要节省ram资源,可以使用malloc()动态申请内存,使用完再用free()释放掉,free()释放的是指针指向的内存空间,而不是指针. 2.如果某个大数组要在两个函数中使用,可以先定义一个全局 ...

  7. lnmp重置mysql数据库root密码

    第一种方法:用军哥的一键修改LNMP环境下MYSQL数据库密码脚本 一键脚本肯定是非常方便.具体执行以下命令: wget http://soft.vpser.net/lnmp/ext/reset_my ...

  8. BZOJ 4823: [Cqoi2017]老C的方块

    分析: 我觉得我的网络流白学了...QAQ... 其实数据范围本是无法用网络流跑过去的,然而出题者想让他跑过去,也就跑过去了... 看到题目其实感觉很麻烦,不知道从哪里入手,那么仔细观察所给出的有用信 ...

  9. bzoj 1060 贪心

    设根到每个叶子节点的距离为dis,比较容易的看出来,我们需要把这颗树的所有叶子节点的值都变成其中最大的内个,我们设为max,那么对于一颗子树来说,设其中dis值最大的为x,我们需要将这个子树根节点和子 ...

  10. bzoj 1601 最小生成树

    原题传送门http://www.lydsy.com/JudgeOnline/problem.php?id=1601 最小生成树的比较水的题,我们只需要加一个源点,连向所有的点,边权为每个点建水库的代价 ...