题目:https://vjudge.net/problem/UVA-1599

解题思路:


为什么不能只用正向的bfs呢?

因为这道题是要使路径上color的字典序最小,所以最终路径上从顶点1出发的这条边的color一定是与顶点1相连的所有边color中的最小值,但是很有可能选择这条最小color边后无法到达终点,如下图:

 

双向bfs怎么用?

1.先从终点n出发,反向bfs,一旦在遍历过程中遇到起点1,则结束遍历,如下图:

入队标记数组inqueue[]   访问数组vis[]    距离(距终点长度)数组d[]

d[6]=0,其他结点都初始化为-1

以下为模拟过程://注意是在取队首时才标记其已访问,故访问邻边(u->v)的判断条件为(!vis[v]&&!inqueue[v])

6入队   inqueue[6]=1;

取队首6,vis[6]=1;

d[3]=d[4]=d[5]=d[6]+1=1,并将3,4,5依次入队;inqueue[3]=inqueue[4]=inqueue[5]=1;

取队首3,vis[3]=1;

d[2]=d[3]+1=2;

取队首4,vis[4]=1;

d[1]=d[4]+1=2;此时遇到起点1,结束反向bfs

故最终路径的长度为d[1]=2;

2.从起点1开始bfs,找到邻边中color最小且该条边(u->v)在1->n的路径上,即满足(d[u]-1=[v]),最小的color计为minc

如果邻边中有多条边color值=minc且这些边在1->n的路径上且另一个端点未访问未入队,则把这些边的另一个端点入队。

index=d[1]-d[u],如当u为起点1时,index=0,当u为路径上1的下一个点时,index=1,用col[index]记录最终要输出的color最小字典序,要不断更新col[index]的最小值(题目中的input正好可以体现更新col[index]的最小值)

若当前队列队首为重点,则结束正向bfs

 

为什么其他结点的d[]值都初始为-1,d[n]初始为0?

如下图:

在反向bfs时,已记录d[1]=1;

若所有的结点d[]都初始化为0时,当正向bfs时,minc=1,d[1]-1=d[2];col[0]=minc=1;且后续col[0]不会再更新

而事实上col[0]=4,这种情况就要把除终点n外的其他结点d都初始为-1

但是,d[]都初始为0在UVA上并不会WA,所以这道题实际上还是有点小bug的

 

ac代码:


#include <iostream>
#include <cmath>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <stdlib.h>
#include <vector>
#include <stack>
#include <queue>
#include <set>
#include <map>
#include <sstream>
#define maxn 100005
#define inf 1e+9+10
using namespace std;
typedef long long ll;
int n,m,i;
typedef struct Arc{
    int num,color;//边的另一个端点和边上的颜色值
    Arc(int n=0,int c=0):num(n),color(c){}
}arc;
vector<arc> edges[maxn];//与每个结点连接的边存入vector中
int d[maxn],col[maxn],vis[maxn],inqueue[maxn];
void bfs(int start)//u->v
{
    memset(vis,0,sizeof(vis));
    memset(inqueue,0,sizeof(inqueue));
    int v,u,len;
    queue<int> q;
    q.push(start);
    inqueue[n]=1;
    if(start==n)//反向bfs
    {
        while(!q.empty())
        {
            u=q.front();q.pop();vis[u]=1;
            len=edges[u].size();
            for(i=0;i<len;i++)
            {
                int v=edges[u][i].num;
                if(!vis[v]&&!inqueue[v])//未访问未入队
                {
                    d[v]=d[u]+1;
                    if(v==1) return ;//找到起点
                    inqueue[v]=1;
                    q.push(v);
                }

            }
        }
    }
    else//正向bfs
    {
        memset(col,0,sizeof(col));
        while(!q.empty())
        {
            u=q.front();q.pop();vis[u]=1;
            if(u==n) return ;//找到终点
            len=edges[u].size();
            int minc=inf;
            for(i=0;i<len;i++)
            {
                v=edges[u][i].num;
                if(!vis[v]&& d[u]-1==d[v])//路径中存在u->v
                    minc=min(minc,edges[u][i].color);//获取u的邻边中color的最小值
            }
            for(i=0;i<len;i++)
            {
                v=edges[u][i].num;
                if(!vis[v]&& (d[u]-1==d[v]) &&!inqueue[v] && edges[u][i].color==minc)
                {
                    inqueue[v]=1;
                    q.push(v);//多组color值相同且未入队的,要入队
                }
            }
            int index=d[1]-d[u];//求第index+1个color值
            if(col[index]==0) col[index]=minc;//未赋值
            else col[index]=min(minc,col[index]);//更新col[index]
        }
    }
}
int main()
{
    //freopen("/Users/zhangkanqi/Desktop/11.txt","r",stdin);
    //ios::sync_with_stdio(false);
    int a,b,c;
    while(scanf("%d %d",&n,&m)==2)
    {
        memset(d,-1,sizeof(d));
        d[n]=0;
        for(i=1;i<=n;i++)
            edges[i].clear();//不重新定义edges[],清空即可
        while(m--)
        {
            scanf("%d %d %d",&a,&b,&c);
            if(a!=b)//排除自环
            {
                edges[a].push_back(arc(b,c));
                edges[b].push_back(arc(a,c));
            }
        }
        bfs(n);
        bfs(1);
        printf("%d\n%d",d[1],col[0]);
        for(i=1;i<d[1];i++)
            printf(" %d",col[i]);
        printf("\n");
    }
    return 0;
}