理想路径——双向BFS

题目

给n个点m条边(2 ≤ n ≤ 100000,1 ≤ m ≤ 200000)的无向图,每条边上都涂有一种颜色。求从结点1到结点n的一条路径,使得经过的边数尽量的少,在此前提下,经过边的颜色序列的字典序最小。一对结点间可能有多条边,一条边可能连接两个相同的结点。输入保证结点1可以到达结点n。颜色为1~109的整数。

解题思路

方法是从终点开始倒着BFS,得到每个结点 i 到终点的最短距离d[i]。然后直接从起点开始走,但是每次到达一个新结点时要保证d值恰好减少1,直到到达终点,这样得到的一定是一条最短路。

有了上述结论,可以这样解决:直接从起点开始按照上述规则走,如果有多种走法,选择颜色字典序最小的走;如果有多条边的颜色字典序都最小,则记录所有这些边的终点,走下一步时要考虑从所有这些点出发的边。这实际上是又做了一次BFS,因此时间复杂度仍为 O(m)。

代码实现

  1 #include<stdio.h>
  2 #include<iostream>
  3 #include<algorithm>
  4 #include<cstring>
  5 #include<vector>
  6 #include<queue>
  7 using namespace std;
  8 
  9 const int maxn = 100000 + 10;
 10 vector<int>G[maxn];
 11 vector<int>C[maxn];
 12 int n, m,vis[maxn], d[maxn], ans[maxn];        //d保存距离,ans保存最小距离
 13 
 14 void init()
 15 {
 16     int x, y;
 17     int tmp;
 18     memset(vis, 0, sizeof(vis));
 19     memset(d, 0, sizeof(d));
 20     memset(ans, 0, sizeof(ans));
 21     for (int i = 0; i <=n; i++)  G[i].clear();
 22     for (int i = 0; i <= n; i++)  C[i].clear();
 23     for (int i = 0; i < m; i++)
 24     {
 25         cin >> x >> y;
 26         G[x].push_back(y); G[y].push_back(x);
 27         cin >> tmp;
 28         C[x].push_back(tmp); C[y].push_back(tmp);
 29     }
 30 }
 31 
 32 void bfs1()    //进行距离的遍历,得到d数组
 33 {
 34     memset(d, -1, sizeof(d));
 35     queue<int>q;
 36     d[n] = 0;
 37     q.push(n);
 38     while (!q.empty())
 39     {
 40         int u = q.front(); q.pop();
 41         int sz = G[u].size();
 42         for (int i = 0; i < sz; i++)
 43         {
 44             int v = G[u][i];
 45             if (d[v] == -1)
 46             {
 47                 d[v] = d[u] + 1;
 48                 q.push(v);
 49             }
 50         }
 51     }
 52     return;
 53 }
 54 
 55 void bfs2()    //对颜色进行排序,并保存颜色
 56 {
 57     memset(vis, 0, sizeof(vis));
 58     queue<int>q;
 59     q.push(1);
 60     while (!q.empty())
 61     {
 62         int u = q.front(); q.pop();
 63         if (d[u] == 0)  return;
 64         int sz = G[u].size();
 65         int mm = -1;
 66         for (int i = 0; i < sz; i++)    
 67         {
 68             int v = G[u][i];
 69             if (d[v] == d[u] - 1)
 70             {
 71                 if (mm == -1)  mm = C[u][i];
 72                 else  mm = min(mm, C[u][i]);
 73             }
 74         }
 75         int t = d[1] - d[u];
 76         if (ans[t] == 0)  ans[t] = mm;
 77         else  ans[t] = min(ans[t], mm);
 78 
 79         for (int i = 0; i < sz; i++)   //将所有同时满足条件的节点加入队列,并同时进行bfs
 80         {
 81             int v = G[u][i];
 82             if (vis[v] == false && d[v] == d[u] - 1 && C[u][i] == mm)
 83             {
 84                 q.push(v);
 85                 vis[v] = true;
 86             }
 87         }
 88     }
 89     return;
 90 }
 91 
 92 int main()
 93 {
 94     while (scanf("%d%d",&n,&m) == 2)
 95     {
 96         init();
 97         bfs1();
 98         bfs2();
 99         printf("%d\n", d[1]);
100         for (int i = 0; i < d[1]; i++)
101         {
102             if (i)  printf(" ");
103             printf("%d", ans[i]);
104         }
105         printf("\n");
106     }
107     return 0;
108 }

参考链接:https://blog.csdn.net/cfarmerreally/article/details/52128440

 

转载于:https://www.cnblogs.com/lfri/p/9726629.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值