传递闭包的应用

传递闭包的应用

传递闭包是离散数学中的概念,先(不严谨的)给出一些定义:

  1. 笛卡尔积:两集合中的元素自由组成全部有序二元组(学名叫序偶)的集合
    A = { 1 , 2 , 3 } , B = { a , b } , 则 A × B = { < 1 , a > . < 1 , b > . < 2 , a > . < 2 , b > . < 3 , a > . < 3 , b > } A=\{1,2,3\},B=\{a,b\},则A\times B=\{<1,a>.<1,b>.<2,a>.<2,b>.<3,a>.<3,b>\} A={1,2,3},B={a,b},A×B={<1,a>.<1,b>.<2,a>.<2,b>.<3,a>.<3,b>}
  2. 关系: A × B A\times B A×B的任意一个子集称 R R R为从 A A A B B B的一个关系, A × A A\times A A×A的一个子集 R R R称为 A A A上的一个关系
    比如小于实数集上的一个关系
  3. 传递性: ∀ x , y , z ∈ A \forall x,y,z \in A x,y,zA,如果 < x , y > ∈ R <x,y>\in R <x,y>R < y , z > ∈ R , 那 么 < x , z > ∈ R <y,z>\in R,那么<x,z>\in R <y,z>R,<x,z>R,则称关系R具有传递性。
    比如小于关系,包含关系都具有传递性
    来自知乎的一张图,感兴趣也可以去里面补一下离散数学1
    在这里插入图片描述

那么,我现在给出一个集合 A = { a , b , c , d } A=\{a,b,c,d\} A={a,b,c,d},给出关系R代表小于关系, R = { < a , b > , < b , c > , < b , d > , < c , d > } R=\{<a,b>,<b,c>,<b,d>,<c,d>\} R={<a,b>,<b,c>,<b,d>,<c,d>} ( 即 给 出 a < b , b < c , . . . ) (即给出a<b,b<c,...) (a<b,b<c,...),那么求我能通过现在已知条件得到的所有小于关系。这个问题就是求关系 R R R传递闭包

将关系 R R R通过一张图表示,集合中的元素是图的点,关系中的元素是图的有向边。将图用邻接矩阵表示,剩下的就是求图上任意两点是否可达了。

(图文无关,仅仅是展示一下将关系转换为图及其邻接矩阵,传递闭包)
在这里插入图片描述

我们把可以求多源最短路的floyd算法变个形(得到的算法叫warshall):
主要思路与floyd类似:

  1. 如果 a a a b b b有一条单向边,那么 a a a b b b可达
  2. 如果可以通过中间节点 c c c a a a b b b,那么 a a a b b b可达
  3. 进而,如果可以通过中间节点 c , d , . . . , z c,d,...,z c,d,...,z a a a b b b,那么 a a a b b b可达

代码是三层循环,最外层枚举的是中间节点,复杂度 O ( n 3 ) O(n^3) O(n3)

for (int k = 0; k < maxn; k++) {
	for (int i = 0; i < maxn; i++) {
		for (int j = 0; j < maxn; j++) {
			less[i][j] |= less[i][k] && less[k][j];
		}
	}
}

最后放道例题:

GCPC2017D.Pants on Fire

唐纳德·特朗普喜欢发表各种言论:“俄罗斯人不如美国人,墨西哥人不如加拿大人,…”但是其中的一些是fake news。现在给定 n ( 1 ≤ n ≤ 200 ) n(1\leq n \leq200) n(1n200)条NYT实锤的新闻,给定 m ( 1 ≤ m ≤ 200 ) m(1\leq m \leq200) m(1m200)条川普说的话,判断川普的话是真话(Fact),是反话(Alternative Fact),还是fake news(Pants on Fire).

用map整一个从string到int的映射,然后建图,求传递闭包。然后即可 O ( 1 ) O(1) O(1)判断是真,是反,还是fake news。
(虽然我觉得这题大可不必这样做,邻接表(链式前向星)存图,跑一遍拓扑排序,再跑一个bfs分层,感觉(指这个思路我也没试过)比这个做法强多了,毕竟拓扑排序和bfs的复杂度都可以到 O ( ∣ V ∣ + ∣ E ∣ ) O(|V|+|E|) O(V+E),可比 O ( n 3 ) O(n^3) O(n3)高到不知道哪去了)

AC代码:

#include <bits/stdc++.h>
using namespace std;

const int maxn = 193;
bool worse[maxn][maxn];

map<string, int> _m;

int get(string s) {
	if (!_m.count(s)) 
		_m[s] = _m.size();
	return _m[s];
}

int main() 
{
	int n, m;
	scanf("%d%d", &n, &m);
	for (int i = 0; i < maxn; i++) {
		for (int j = 0; j < maxn; j++) {
			worse[i][j] = 0;
		}
	}
	for (int i = 0; i < n; i++) {
		string A, x, y, z, B;
		cin >> A >> x >> y >> z;
		cin >> B;
		worse[get(A)][get(B)] = 1;
	}
	// transitive hull
	for (int k = 0; k < maxn; k++) {
		for (int i = 0; i < maxn; i++) {
			for (int j = 0; j < maxn; j++) {
				worse[i][j] |= worse[i][k] && worse[k][j];
			}
		}
	}

	for(int i = 0; i < m; i++) {
		string A, x, y, z, B;
		cin >> A >> x >> y >> z;
		cin >> B;
		if (worse[get(A)][get(B)])
			cout << "Fact" << endl;
		else if (worse[get(B)][get(A)])
			cout << "Alternative Fact" << endl;
		else
			cout << "Pants on Fire" << endl;
	}
}

  1. https://zhuanlan.zhihu.com/p/57478122 ↩︎

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值