B3611 【模板】传递闭包
首先,要弄清楚传递闭包的定义,由题意:
一张图的邻接矩阵定义为一个 n × n n\times n n×n 的矩阵 A = ( a i j ) n × n A=(a_{ij})_{n\times n} A=(aij)n×n,其中
a i j = { 1 , i 到 j 存在直接连边 0 , i 到 j 没有直接连边 a_{ij}=\left\{ \begin{aligned} 1,i\ 到\ j\ 存在直接连边\\ 0,i\ 到\ j\ 没有直接连边 \\ \end{aligned} \right. aij={1,i 到 j 存在直接连边0,i 到 j 没有直接连边
一张图的传递闭包定义为一个 n × n n\times n n×n 的矩阵 B = ( b i j ) n × n B=(b_{ij})_{n\times n} B=(bij)n×n,其中
b i j = { 1 , i 可以直接或间接到达 j 0 , i 无法直接或间接到达 j b_{ij}=\left\{ \begin{aligned} 1,i\ 可以直接或间接到达\ j\\ 0,i\ 无法直接或间接到达\ j\\ \end{aligned} \right. bij={1,i 可以直接或间接到达 j0,i 无法直接或间接到达 j
如果我们把参数的含义改变一下,就可以很容易地发现本题其实是多源最短路径问题。
名称 | 改变前 | 改变后 |
---|---|---|
a i j = 1 a_{ij} = 1 aij=1 | i i i 到 j j j 有直接连边 | e ( i , j ) e(i, j) e(i,j) 存在且权值为0 |
a i j = 0 a_{ij} = 0 aij=0 | i i i 到 j j j 无直接连边 | e ( i , j ) e(i, j) e(i,j) 不存在 |
b i j = 1 b_{ij} = 1 bij=1 | i i i 可以直接或间接到达 j j j | i i i 到 j j j 存在最短路径 |
b i j = 0 b_{ij} = 0 bij=0 | i i i 无法直接或间接到达 j j j | i i i 到 j j j 不存在最短路径 |
因此,我们可以采取 F l o y d − W a r s h a l l Floyd-Warshall Floyd−Warshall 解决。
#include
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;
}
注意:
模板中, d [ i ] [ j ] = d[i][j] = d[i][j]= i i i 到 j j j 的最短路径。现在,令
d [ i ] [ j ] = { t r u e , i 可以到 j f a l s e , i 不可以到 j d[i][j] = \begin{cases} true &, i 可以到 j \\ false &, i 不可以到 j \end{cases} d[i][j]={truefalse,i可以到j,i不可以到j
F l o y d − W a r s h a l l Floyd-Warshall 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 i i 能否到 j j j,有两种情况:1、 i i i 能否直接到 j j j;2、 i i i 能否由 k k k 中转到 j j j。有一种成立, i i i 就能到 j j j。
#include
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;
}
完