D : 图的顶点可达闭包

Description

给定有向图的邻接矩阵A,其元素定义为:若存在顶点i到顶点j的有向边则A[i,j]=1,若没有有向边则A[i,j]= 0。试求A的可达闭包矩阵A*,其元素定义为:若存在顶点i到顶点j的有向路径则A*[i,j]=1,若没有有向路径则A*[i,j]= 0

Input

第1行顶点个数n
第2行开始的n行有向图的邻接矩阵,元素之间由空格分开

Output

有向图的可达闭包矩阵A*,元素之间由空格分开

Sample

Input

4
0 1 0 1
0 0 1 0
0 0 0 0
0 0 0 0

Output

0 1 1 1
0 0 1 0
0 0 0 0
0 0 0 0

解题思路

一开始我不知道闭包是啥东西,查了一下,通俗点讲就是:如果在图中存在一个从顶点i到顶点j的有向路径(不论这个路径有多长),则在A中,元素A[i, j] = 1。知道了这个之后就会想到Floyd算法,这个算法是**考虑所有可能的中间顶点,并逐步更新每一对顶点之间的最短路径。**放在这道题就是从顶点i到顶点j之间的所有可能的顶点,全部逐步拼接起来。先来看看Floyd算法:

int data[maxn][maxn];
void Floyd(int n)
{
	for (int k = 0; k < n; k++)
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n; j++)
                data[i][j]=min(data[i][j], data[i][k]+data[k][j]);
}

三重循环的工作原理

  1. 外层循环(遍历中间顶点k)

    • 这个循环遍历图中的每一个顶点,将其作为潜在的“中间顶点”。这里的“中间顶点”是指可能位于一条从顶点i到顶点j路径上的任何顶点。每一次循环都会考虑通过这个中间顶点k的所有可能路径。
  2. 中间层循环(遍历起始顶点i)

    • 对于每一个顶点k,这个循环遍历图中的每一个顶点i,将其作为路径的起始点。这个循环的目的是考虑从顶点i出发,经过顶点k,到达任意顶点j的所有可能路径。
  3. 内层循环(遍历结束顶点j)

    • 对于每一对顶点i和k,这个循环遍历图中的每一个顶点j,将其作为路径的结束点。这个循环的目的是完成路径的考虑,检查是否通过顶点k可以找到从i到j的更短路径。

如何更新距离矩阵

在这三个循环中,距离矩阵dist[i][j]表示从顶点i到顶点j的当前已知最短路径长度。在每次迭代中,算法馋死通过中间顶点k来改进这个距离。具体的,算法检查是否dist[i][k] + dist[k][j](即通过k的路径长度)小于当前的dist[i][j]。如果是,那么就更新dist[i][j]为这个较小的值。

这意味着算法在每次迭代中都在考虑加入一个新的中间顶点,看是否可以通过这个新的中间顶点找到更短的路径。通过逐步考虑所有可能的中间顶点,算法能够确保找到所有顶点对之间的最短路径。

为什么k放在第一个循环

将k放在最外层循环是逐步构建最短路径的关键。这种方法允许算法首先考虑所有通过单个中间顶点的路径,然后是通过两个中间顶点的路径,以此类推,直到考虑了通过所有中间顶点的路径。这样做确保了算法能够逐步建立起从每个起始点到每个目的地的最短路径,考虑了所有可能的中间步骤。

换句话说,k在第一个循环中意味着你首先考虑所有可能的捷径(中间城市),然后基于这些捷径找出所有的最短路线。确保没有遗漏任何可能的捷径,从而可以找到从任何一个城市到达另一个城市的真正最短路线。

说了这么多其实这就是floyd算法的思想,知道了这个思想就可以直接求解这一道题,但是这一道题不需要求解最短路径,只要说明有路可走,同时动态规划的方程不适用了。上代码看吧:

AC代码:

#include <iostream>
using namespace std;
const int MAX_N = 50;
void floyd(int graph[][MAX_N], int n) {
    int dist[MAX_N][MAX_N];

    // 初始化距离矩阵
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            dist[i][j] = graph[i][j];
        }
    }

    // 弗洛伊德算法
    for (int k = 0; k < n; k++) {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                // 如果i到k和k到j之间有路径,则设置i到j有路径
                if (dist[i][k] == 1 && dist[k][j] == 1) {
                    dist[i][j] = 1;
                }
            }
        }
    }

    // 打印可达闭包矩阵
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cout << dist[i][j] << " ";
        }
        cout << endl;
    }
}

int main() {
    int n;
    cin >> n;

    int graph[MAX_N][MAX_N];

    // 邻接矩阵
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            cin >> graph[i][j];
        }
    }

    // 计算并打印可达闭包矩阵
    floyd(graph, n);
    return 0;
}

11-24 20:10