传递闭包问题

一、问题描述

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 FloydWarshall 解决。


三、本题代码

3.1 直接套用 F l o y d − W a r s h a l l Floyd-Warshall FloydWarshall 模板

#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 ] = { e ( i , j ) . w , e ( i , j ) 存在且 i ≠ j I N F , e ( i , j ) 不存在或 i = = j d[i][j] = \begin{cases} e(i, j).w &, e(i, j)存在且 i \neq j \\ INF &,e(i, j)不存在或 i == j \end{cases} d[i][j]={e(i,j).wINF,e(i,j)存在且i=j,e(i,j)不存在或i==j
    输入为 1,表示边存在;输入为 0,表示边不存在。需要注意自环是不存在的。
  • 2、要输出得不是最短路径,而是传递闭包。令
    p r i n t f = { 1 , d [ i ] [ j ] ≠ I N F 0 , d [ i ] [ j ] = = I N F \mathbf{printf} = \begin{cases} 1 &,d[i][j] \neq \mathbf{INF} \\ 0 &,d[i][j] == \mathbf{INF} \end{cases} printf={10,d[i][j]=INF,d[i][j]==INF

3.2 稍微改进一点

模板中, 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 FloydWarshall 的核心 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 <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;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值