传递闭包

一、问题描述

B3611 【模板】传递闭包

二、问题简析

首先,要弄清楚传递闭包的定义,由题意:

一张图的邻接矩阵定义为一个 \(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;
}

posted @   ltign  阅读(85)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示