传递闭包
一、问题描述
二、问题简析
首先,要弄清楚传递闭包的定义,由题意:
一张图的邻接矩阵定义为一个 \(n\times n\) 的矩阵 \(A=(a_{ij})_{n\times n}\),其中
\[ a_{ij}=\left\{
\begin{aligned}
1,i\ 到\ j\ 存在直接连边\\
0,i\ 到\ j\ 没有直接连边 \\
\end{aligned}
\right.
\]
一张图的传递闭包定义为一个 \(n\times n\) 的矩阵 \(B=(b_{ij})_{n\times n}\),其中
\[ b_{ij}=\left\{
\begin{aligned}
1,i\ 可以直接或间接到达\ j\\
0,i\ 无法直接或间接到达\ j\\
\end{aligned}
\right.
\]
如果我们把参数的含义改变一下,就可以很容易地发现本题其实是多源最短路径问题。
名称 | 改变前 | 改变后 |
---|---|---|
\(a_{ij} = 1\) | \(i\) 到 \(j\) 有直接连边 | \(e(i, j)\) 存在且权值为0 |
\(a_{ij} = 0\) | \(i\) 到 \(j\) 无直接连边 | \(e(i, j)\) 不存在 |
\(b_{ij} = 1\) | \(i\) 可以直接或间接到达 \(j\) | \(i\) 到 \(j\) 存在最短路径 |
\(b_{ij} = 0\) | \(i\) 无法直接或间接到达 \(j\) | \(i\) 到 \(j\) 不存在最短路径 |
因此,我们可以采取 \(Floyd-Warshall\) 解决。
三、本题代码
3.1 直接套用 \(Floyd-Warshall\) 模板
#include <bits/stdc++.h>
using namespace std;
#define MdX 103
#define INF 1e8
int n;
int d[MdX][MdX];
void solve(void)
{
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
fill(begin(d[i]), end(d[i]), INF);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
{
int a;
scanf("%d", &a);
if (a != 0)
{
d[i][j] = a;
}
}
solve();
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (d[i][j] == INF)
printf("%d ", 0);
else
printf("%d ", 1);
}
putchar('\n');
}
return 0;
}
注意:
- 1、初始化连接矩阵时要格外注意,与要来的算法不太一样。令
\[d[i][j] = \begin{cases}
e(i, j).w &, e(i, j)存在且 i \neq j \\
INF &,e(i, j)不存在或 i == j
\end{cases}
\]
输入为 1,表示边存在;输入为 0,表示边不存在。需要注意自环是不存在的。
- 2、要输出得不是最短路径,而是传递闭包。令
\[\mathbf{printf} = \begin{cases}
1 &,d[i][j] \neq \mathbf{INF} \\
0 &,d[i][j] == \mathbf{INF}
\end{cases}
\]
3.2 稍微改进一点
模板中,\(d[i][j] =\) \(i\) 到 \(j\) 的最短路径。现在,令
\[d[i][j] = \begin{cases}
true &, i 可以到 j \\
false &, i 不可以到 j
\end{cases}
\]
\(Floyd-Warshall\) 的核心 d[i][j] = min(d[i][j], d[i][k] + d[k][j])
,变成 d[i][j] |= d[i][k] & d[k][j]
。可以这样理解:\(i\) 能否到 \(j\),有两种情况:1、\(i\) 能否直接到 \(j\);2、\(i\) 能否由 \(k\) 中转到 \(j\)。有一种成立,\(i\) 就能到 \(j\)。
#include <bits/stdc++.h>
using namespace std;
#define MAX 103
int n;
bool d[MAX][MAX];
void solve(void)
{
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
d[i][j] |= d[i][k] & d[k][j];
}
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
scanf("%d", &d[i][j]);
solve();
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
printf("%d ", d[i][j]);
putchar('\n');
}
return 0;
}
完
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)