传递闭包的应用
传递闭包是离散数学中的概念,先(不严谨的)给出一些定义:
- 笛卡尔积:两集合中的元素自由组成全部有序二元组(学名叫序偶)的集合
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>} - 关系:
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上的一个关系
比如小于是实数集上的一个关系 - 传递性:
∀
x
,
y
,
z
∈
A
\forall x,y,z \in A
∀x,y,z∈A,如果
<
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类似:
- 如果 a a a到 b b b有一条单向边,那么 a a a到 b b b可达
- 如果可以通过中间节点 c c c从 a a a到 b b b,那么 a a a到 b b b可达
- 进而,如果可以通过中间节点 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(1≤n≤200)条NYT实锤的新闻,给定 m ( 1 ≤ m ≤ 200 ) m(1\leq m \leq200) m(1≤m≤200)条川普说的话,判断川普的话是真话(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;
}
}
https://zhuanlan.zhihu.com/p/57478122 ↩︎