排序 Floyd-计算传递闭包
题目
思路
考虑\(d[i][j]\)只有两种边权\(0/1\), \(0\)表示\(i\)和\(j\)的关系未知, \(1\)表示\(i < j\) (即\(d[i][j]\)表示两点之间是否连同的问题)
第一种情况是已经排序好,第二种是存在环的情况 (例如\(i < j\) ,\(j < i\)), 在排除第二种的情况下我们只需要判断每个点到其他点的距离是否已经已知,即对任意的\((i, j) 满足 d[i][j] 或 d[j][i]\)
由于数据量很小我们可以考虑Floyd, 复杂度\(O(N^3 \times m)\)
由于每次只更新一条边,我们可以考虑优化上述算法:
考虑新加入的点是\(u\rightarrow v\), 由于我们考虑更新过程中无环才继续更新的,所以我们更新只考虑\(d[x][u]\)和\(d[v][y]\)为\(1\)的点进行更新,另外更新过程中将\(d[x][u]为1的点更新d[x][v]为1\),将\(d[v][y]为1的点更新d[u][y]为1\) , 复杂度为\(O(N^2 \times m)\)
Code
#include <bits/stdc++.h>
using i64 = long long;
const int N = 30;
int n, m;
bool d[N][N];
void one_floyd(int u, int v) {
// for (int k = 0; k < n; k ++) {
// for (int i = 0; i < n; i ++) {
// for (int j = 0; j < n; j ++) {
// d[i][j] |= d[i][k] && d[k][j];
// }
// }
// }
for (int i = 0; i < n; i ++) {
if (d[i][u]) d[i][v] = 1;
if (d[v][i]) d[u][i] = 1;
for (int j = 0; j < n; j ++) {
if (d[i][u] && d[v][j]) {
d[i][j] = 1;
}
}
}
}
int check() {
for (int i = 0; i < n; i ++) {
// std::cout << d[i][i] << " \n"[i == n - 1];
if (d[i][i])
return 2;
}
for (int i = 0; i < n; i ++)
for (int j = 0; j < i; j ++)
if (!d[i][j] && !d[j][i])
return 0;
return 1;
}
int main() {
while (std::cin >> n >> m, n || m) {
memset(d, 0, sizeof d);
int type = 0, cnt = 0;
for (int i = 1; i <= m; i ++) {
std::string t;
std::cin >> t;
int a = t[0] - 'A', b = t[2] - 'A';
if (!type) {
d[a][b] = 1;
one_floyd(a, b);
type = check();
// std::cout << type << "\n";
if (type) cnt = i;
}
}
std::string ans;
for (int i = 0; i < n; i ++) ans.push_back(i + 'A');
if (!type) puts("Sorted sequence cannot be determined.");
else if (type == 2) printf("Inconsistency found after %d relations.\n", cnt);
else {
std::sort(ans.begin(), ans.end(), [](char a, char b) {
return d[a - 'A'][b - 'A'];
});
printf("Sorted sequence determined after %d relations: %s.\n", cnt, ans.c_str());
}
}
}