图的深度优先和广度优先遍历

图的深度优先和广度优先遍历

题目描述:
暑假小哼想到小哈家玩,小哼和小哈住在不同的城市,并且小哼之前从来没有去过小哈家,这是小哼第一次上门。怎么去呢?小哼查了下百度地图,城市地图如下:

2
10
3
4
4
5
7
3
1
2
5
3
4

数据是这样给出的,如下。

             5 8
             1 2 2
             1 5 10
             2 3 3 
             2 5 7
             3 1 4
             3 4 4
             4 5 5 
             5 3 3

第一行的5表示有5个城市(城市编号为1~5),8表示有8条公路。接下来8行每行是一条类似“a b c”这样的数据,表示有一条路可以从城市a到城市b,并且路程为c公里。需要注意的是这里的公路都是单行的,即“a b c”仅仅表示一条路可以从城市a到城市b,并不表示城市b也有一条路可以到城市a。小哼家在1号城市,小哈家在5号城市。现在请求出1号城市到5号城市的最短路程(也叫做最短路径)。

深度优先遍历就是用一个二维矩阵将城市之间的路程存储起来,相同城市路程为0,不能到达的城市用一个尽量大的数表示。然后就从一号城市开始深搜,到达五号城市为止,求出最短路径。
代码如下:

#include<iostream>
#include<cstdio>
#define maxPath 99999999
using namespace std;
int e[1001][1001];
int book[1001];
int Min = 999999999;
int m,n; //m城市数,n路径数 
void dfs(int cur,int dis)
{
// cout << dis << endl;
   if(dis >= Min)
   return;
   if(cur == m)
   {
    if(dis < Min)
    Min = dis;
    return;
   }
   for(int k = 1; k <= m; k ++)
   {
    if( e[cur][k] != maxPath && book[k] == 0)
    {
       book[k] = 1;
         dfs(k,dis + e[cur][k]);
    book[k] = 0;  
 }
   }
}
int main()
{
 
   cin >> m >> n;
   for(int i = 1; i <= m; i ++)
   for(int j = 1; j <= m; j ++)
   {
      if(i == j)
 e[i][j] = 0;
 else
 e[i][j] = maxPath; 
   } 
   for(int i = 1; i <= n; i ++)
   {
      int x,y,s;
 scanf("%d%d%d",&x,&y,&s);
 e[x][y] = s; 
   }
   
   book[1] = 1;
   dfs(1,0); 
   cout << Min;
   return 0;
} 

本来是想用广度优先遍历求转点,发现没什么意义。这里介绍一种算法来求任意两点间的最短路径。
这个算法叫做Floyd-Warshall,虽然这个算法只有短短五行,但思想要理解。

for(int i = 1; i <= n; i ++)
for(int j = 1; j <= n; j ++)
{
   if(e[i][j] > e[i][1] + e[1][j])
   e[i][j] = e[i][1] + e[1][j];
}

上面这段代码中 e[i][j] 表示的是 i 号顶点到 j 号顶点路径。e[i][1] + e[1][j] 表示的是从 i 号顶点到1号顶点,再从1号顶点到 j 号顶点的路程之和。也就是说这段代码是指只允许通过1号顶点时,任意两点间的路程更新。由此类推,可得到以下该算法核心代码。

for(int k = 1; k <= n; k ++)
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= n; j ++)
{
   if(e[i][j] > e[i][1] + e[1][j])
   e[i][j] = e[i][1] + e[1][j];
}
····

个单源最短路算法----Dijkstra算法。该算法的基本思想是:每次找到离源点最近的一个顶点,然后以该顶点为中心扩展,最终找到源点到其余所有点的最短路径。基本步骤如下:
1 将所有顶点分为两部分,用book数组记录使用过的顶点。初始时源点book为1
2 用数组dis来记录该源点到其他顶点的路径
3 选择一个未使用过的顶点,dis离源点最近的顶点u。即dis[u]最小,并以该点为进行扩充,用dis[v]去和dis[u]+e[u][v]去比较。
4 重复第三步骤。
完整代码如下:

#include<iostream>
#include<cstdio>
#define maxPath 99999999
using namespace std;
int n,m;
int e[1001][1001];
int dis[1001];
int book[1001];
int main()
{
   cin >> n >> m;
   for(int i = 1; i <= n; i ++)
   for(int j = 1; j <= n; j ++)
      if(i == j)
      e[i][j] = 0;
      else
      e[i][j] = maxPath;
 
   for(int i = 1; i <= m; i ++)
   {
    int x,y,s;
    cin >> x >> y >> s;
    e[x][y] = s;
   }
   for(int i = 1; i <= n; i ++)
   dis[i] = e[1][i];
   book[1] = 1;
   int min,u;
   for(int i = 1; i < n; i ++)
   {
    min = maxPath;
    for(int j = 1; j <= n; j ++)
    {
       if(book[j] == 0 && dis[j] < min)
         {  
        min = dis[j];
   u = j; 
    }    
      }
      book[u] = 1;
      for(int i = 1; i <= n; i ++)
      {
         if(e[u][i] < maxPath)
         if(dis[i] > dis[u] + e[u][i])
    dis[i] = dis[u] + e[u][i]; 
 }
    } 
    for(int i = 1; i <= n; i ++)
    cout << dis[i] << " ";
   return 0;
} 

上面代码求得是源点1到其他各点的最短路径。

发布了26 篇原创文章 · 获赞 0 · 访问量 1359

猜你喜欢

转载自blog.csdn.net/weixin_43846217/article/details/104089207