求有限集传递闭包的 Floyd Warshall 算法(矩阵实现)
zzuoj 1199 题 链接 http://acm.zzu.edu.cn:8000/problem.php?id=1199
Problem B: 大小关系
Time Limit: 2 Sec Memory Limit: 128 MB
Submit: 148 Solved: 31
[Submit][Status][Web Board]
Description
当我们知道一组大小关系之后,可判断所有关系是否都能成立,即关系间没有矛盾。
例如:A<B, A<C, B<C 通过这组关系我们可以得到A<B<C ,所有关系都成立,没有矛盾。
若 A<B, B<C, C<A 通过前两个关系我们得到 A<B<C ,这个关系与C<A矛盾,所有关系不能同时成立。
现在我们知道m个关系,请判断这m个关系是否能成立,成立输出“YES”,否则输出“NO”。
Input
多组数据,每组数据如下:
第一行有一个数字m。 m代表m组关系(1<=m<=400),接下来m行每行有一个关系,用两个不同的字母和一个符号表示。(输入保证字母在‘A’-‘Z’之间,关系符号只有 > , <)
Output
对于每组数据输出“YES”或“NO”.
Sample Input
3
Sample Output
YES
NO
/*********************************************************题解**************************************************************/
大于和小于 其实是一种关系,即两个元素之间的关系,b>a 可以用 a < b 来描述。
题目中所给的就是26
个字母之间的关系,我们可以用0 到25 的整数对应 A 到 Z ,用二维数组来表达元素之间的关系,如 re[0][25]==1 就表示 0
< 25 ,即 A < Z。剩下的就只剩下推理的过程了。
学过 离散数学(上海科学技术文献出本社)的人都知道在 集合论 中 关系的性质中 有一种性质 叫做 传递性 而 < 这种关系就正好具有这种性质,当然小于 并不满足 自反性 和 对称性 这是我们推出矛盾的关键。
关系的的闭包运算 中 有一种闭包运算叫做 求关系 R 的 传递闭包 R+ ,这个 R+ 就是传递的,也就是说 如果在 R+ 中找到 a < b 和 b < c 就一定能找到 a < c ,也就是说 R+ 就是推理过后所得到的关系。
求R+ 的算法:
for(i=0; i<26;++i){
for( j=0;j<26;++j){
if(re[ j ][ i ]){
for( k=0;k<26;++k){
re[ j ][ k ] +=re[ i ][ k] ;
}
}
}
}
就这么多,感谢Warshall
顺便提一句,这个Floyd Warshall 和图论里面的那个经典的Floyd 算法的提出者是一个人,其实这道题也完全可以用图论的方式来做
/***************************************/
AC代码:
# include <iostream>
# include <string.h>
# include <stdlib.h>
# include <math.h>
# include <stdio.h>
# include <algorithm>
# include <stack>
int m,re[ 27 ][ 27 ];
char a,b,c,in[ 4 ];
int main(){
using namespace std;
int i,j,k;
while(~scanf("%d",&m)){
memset(re,0,sizeof(re));
for( i=0; i<m; ++i){
scanf("%s",in);
a=in[ 0 ];
c=in[ 1 ];
b=in[ 2 ];
if(c=='<'){
re[ a-'A' ][ b-'A' ]=1;
}else if(c=='>'){
re[ b-'A' ][ a-'A' ]=1;
}
}
for(i=0; i<26;++i){
for( j=0;j<26;++j){
if(re[ j ][ i ]){
for( k=0;k<26;++k){
re[ j ][ k ]+=re[ i ][ k ];
}
}
}
}
for(i=0;i<26;++i){
for(j=0;j<26;++j)
if(re[ i ][ j ]&&re[ j ][ i ]){
cout << "NO\n";
goto kkk;
}
}
cout << "YES\n";
kkk:;
}
return 0;
}