最近在读lyd写得算法竞赛进阶指南,里面收录了Floyd算法的一道例题:排序。
题目如下:
简单介绍一下Floyd算法,Floyd算法需要三层for循环,第一层for循环k是一共要松弛的轮次,内层两个for循环 i、j是代表要处理的起始和目标节点,详细可以看这篇博客:傻子也能看懂的弗洛伊德算法
我们可以使用邻接矩阵来表示变量间的关系,当g[i][j]=true 代表 i<j ,题目的变量是A-Z,所以g[0][1]代表 (0+‘A’)‘A’ 和 (1+‘A’)'B’的关系。这题显然不是要我们求最短距离,但是我们可以把收敛距离的操作变为传递闭包:
//原来
for(k)
for(i)
for(j)
if(g[i][j]>g[i][k]+g[k][j])
g[i][j]=g[i][k]+g[k][j]
//变为
for(k)
for(i)
for(j)
g[i][j]|=g[i][k]&g[k][j];
这样子就可以看出各个变量之间的关系,同时要考虑矛盾和不能确定大小关系的情况:
矛盾: g[i][j]==1&&g[j][i]==1 => a>b && b>a
不能确定大小: g[i][j]==0&&g[j][i]==0 => a?b && b?a
如果不能确定变量之间的大小关系,就加入一个关系,直到能确定关系或者出现矛盾或者所有的关系都用完了。
题目最后要求输出这些变量的大小关系,其实就是图的拓扑排序,但是我们使用了Floyd算法就使用了邻接矩阵,不大好DFS求拓扑排序,所以我们换一个思路:当所有变量能确定关系了,其邻接矩阵应该像下面这样:
A<B B<C
011 A小于B和C
001 B小于C
000 C不小于任何数
所以我们可以遍历得到的邻接矩阵,如果这个变量有k个1,代表它小于k个数字,所以它应该在(n-k-1)的顺序上(下标从0开始)。
完整代码如下:
#include<iostream>
#include<cstring>
#include<string>
#define ac cin.tie(0);cin.sync_with_stdio(0);
using namespace std;
const int MAXN = 30;
bool G[MAXN][MAXN];
int n, m;
int judge() {
for (int k = 0; k < n; k++)
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++) {
G[i][j] |= G[i][k] & G[k][j];
if (G[i][j] && G[i][j] == G[j][i] && i != j)
//conflict
return -1;
}
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
if (!G[i][j] && G[i][j] == G[j][i] && i != j)
//unsure
return 0;
return 1;
}
//get topology sort
void getAns() {
char ans[n];
for (int i = 0; i < n; i++) {
int tmp = 0;
for (int j = 0; j < n; j++)
if (G[i][j])
++tmp;
ans[tmp] = i + 'A';
}
for (int i = n - 1; i >= 0; i--)
putchar(ans[i]);
puts(".");
}
int main() {
ac
string s;
while (cin >> n >> m && n && m) {
bool op = false;
memset(G, false, sizeof(G));
for (int i = 1; i <= m; i++) {
cin >> s;
//build graph
G[s[0] - 'A'][s[2] - 'A'] = true;
if (!op) {
//return value
int res = judge();
if (res == -1) {
printf("Inconsistency found after %d relations.\n", i);
op = true;
} else if (res == 1) {
printf("Sorted sequence determined after %d relations: ", i);
getAns();
op = true;
}
}
}
if (!op)
printf("Sorted sequence cannot be determined.\n");
}
return 0;
}