1.题意
魔法猫告诉 TT,它其实拥有一张游戏胜负表,上面有 N 个人以及 M 个胜负关系,每个胜负关系为 A B,表示 A 能胜过 B,且胜负关系具有传递性。即 A 胜过 B,B 胜过 C,则 A 也能胜过 C。
TT 不相信他的小猫咪什么比赛都能预测,因此他想知道有多少对选手的胜负无法预先得知,你能帮帮他吗?
2.样例
Input
第一行给出数据组数。
每组数据第一行给出 N 和 M(N , M <= 500)。
接下来 M 行,每行给出 A B,表示 A 可以胜过 B。
Output
对于每一组数据,判断有多少场比赛的胜负不能预先得知。注意 (a, b) 与 (b, a) 等价,即每一个二元组只被计算一次
Sample Input
3
3 3
1 2
1 3
2 3
3 2
1 2
2 3
4 2
1 2
3 4
Sample Output
0
0
4
3.解题思路
- 该题为对图上的传递闭包,即任意两点间的联通关系的考察,这也是Floyd算法应用的一个方面
- 关于题意,比赛的胜负具有传递性,A->B&&B->C=>A->C,但是A->B&&C->B不能推出A->C||C->A,所以在实际存储关系时这是一个有向图,只有dis[a][b]=0&&dis[b][a]=0,a,b胜负关系无法预先判断
- 一个二元组只能计算一次所以结果为count/2
4.总结
- Floyd-Warshall算法应用:用于求取图中任意两点之间的关系;多源最短路,任意两点的联通关系;图上的传递闭包,任意两点的连通关系,复杂度为O(n^3)
- 还是要注意传递闭包的逻辑关系
- 关于TL问题,要对Floyd剪枝,三层逻辑k,i,j只要k,i不能得出结论,则第三层循环就没有进行的必要
5.AC代码
#include<iostream>
#include<queue>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
int dis[510][510];
void Floyd(int n)
{
for(int k=1;k<=n;k++)
{
for(int i=1;i<=n;i++)
{
if(dis[i][k]==1)
{
for(int j=1;j<=n;j++)
{
dis[i][j]=max(dis[i][j],min(dis[i][k],dis[k][j]));
//if(dis[i][j]==1) dis[j][i]=1;
}
}
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int k,n,m,a,b;
cin>>k;
for(int t=0;t<k;t++)
{
for(int i=0;i<510;i++)
{
for(int j=0;j<510;j++)
{
if(i==j)
{
dis[i][j]=1;
}
else{
dis[i][j]=0;
}
}
}
cin>>n>>m;
for(int i=0;i<m;i++)
{
cin>>a>>b;
dis[a][b]=1;
//dis[b][a]=1;
}
Floyd(n);
int count=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(i!=j&&dis[i][j]==0&&dis[j][i]==0)
{
count++;
}
}
}
cout<<count/2<<endl;
}
return 0;
}