题333.floyd扩展之求传递闭包-acwing-Q343–排序
一、题目
二、题解
本题要你求n个字母关系的确定情况,关键在于如何判断出现关系矛盾与所有字母关系全部确定。
首先,易知,本题给定小于关系,其中具有传递性,所以本质是在求传递闭包。因此每次加入新的关系后,判断前先跑一遍floyd求当前传递闭包,若dist[i][j]=1则有’A’+i<‘A’+j。
其次,关于判断有没有出现关系矛盾,只要看dist[i][i]是否为1,如果为1,则i<i,就是出现了矛盾。关于判断所有字母关系是否全部确定,可以判断是否有dist[i][j]和dist[j][i]都为0,如果有则说明没有确定所有字母的关系,如果一直都没有则答案不言而喻。
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int maxn=26;
int n,m;
int G[maxn][maxn],dist[maxn][maxn];//G[i][j]=1表示'A'+i与'A'+j直接具备<关系,dist[i][j]=1表示'A'+i<'A'+j
int vis[maxn];
void floyd()//求传递闭包
{
memcpy(dist,G,sizeof dist);//先将dist初始化成G
for(int k=0;k<n;k++)
{
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
dist[i][j]=dist[i][j]||dist[i][k]&&dist[k][j];//若i通过k与j连通或i与j已经连通,则将dist[i][j]赋值为1
}
}
}
}
int check()
{
for(int i=0;i<n;i++)//检查是否存在i<i,有则出现矛盾
{
if(dist[i][i])
{
return -1;
}
}
for(int i=0;i<n;i++)
{
for(int j=0;j<i;j++)//因为考虑i、j和j、i,所以只用考虑到j<i即可,这不仅避免了i=j,还减少了循环次数
{
if(!dist[i][j]&&!dist[j][i])//若i,j和j,i都不存在<的关系,则说明当前没有将n个字母的关系确定,则直接return 0
{
return 0;
}
}
}
return 1;
}
char getNowMin()//得到当前关系处于最小的字母
{
for(int i=0;i<n;i++)
{
if(!vis[i])//当前字母没有被输出过
{
int j;
for(j=0;j<n;j++)
{
if(!vis[j]&&dist[j][i])//如果存在一个没有输出的字母'A'+j比'A'+i的关系更小,则'A'+i不是当前最小
{
break;
}
}
if(j==n)//坚持到了最后,没有一个字母'A'+j的关系比'A'+i更小
{
vis[i]=1;
return 'A'+i;
}
}
}
}
int main()
{
while(cin>>n>>m,n||m)
{
int flag=0,cnt;//flag为0表示还未确定n个字母的关系,为-1表示矛盾,为1表示n个字母关系已经确定
memset(G,0,sizeof G);
for(int i=1;i<=m;i++)
{
string relation;
cin>>relation;
int u=relation[0]-'A',v=relation[2]-'A';
if(!flag)
{
G[u][v]=1;
floyd();
flag=check();
if(flag)
{
cnt=i;
}
}
}
if(flag==-1)
{
printf("Inconsistency found after %d relations.\n",cnt);
}
else if(flag==0)
{
printf("Sorted sequence cannot be determined.\n");
}
else
{
memset(vis,0,sizeof vis);//记得把表示是否已经输出过该字母的数组初始化为0
printf("Sorted sequence determined after %d relations: ",cnt);
for(int i=0;i<n;i++)
{
printf("%c",getNowMin());
}
printf(".\n");
}
}
}
关于floyd求某关系的传递闭包,dist[i][j]表示具有i到j的关系,开始时dist用G直接关系矩阵初始化。代码板子如下:
void floyd()//求传递闭包
{
memcpy(dist,G,sizeof dist);//先将dist初始化成G
for(int k=0;k<n;k++)
{
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
dist[i][j]=dist[i][j]||dist[i][k]&&dist[k][j];//若i通过k与j连通或i与j已经连通,则将dist[i][j]赋值为1
}
}
}
}