Floyd求传递闭包,最小环
https://www.acwing.com/problem/content/345/
acwing 343.排序
Floyd求传递闭包。可以将$A<B$建图为$g[a][b] = 1$。对只有数值$0$和$1$的邻接矩阵跑$Floyd$。
判断的条件有三个:
①当$g[i][j]$ 和 $g[j][i]$都为$0$, 那么就无法确定序列。
②当$g[i][i] = 1$说明有矛盾。
③除以上两种情况外,可以确定序列。
因为每加一条边都要在图上跑一遍$Floyd$所以需要一个数组$d$,每次将$g$复制到$d$,在$d$上跑。
1 #include <iostream> 2 #include <algorithm> 3 #include <cstring> 4 5 using namespace std; 6 7 const int N = 26; 8 9 int g[N][N], d[N][N]; 10 int st[N]; 11 int n, m; 12 13 14 void Floyd() 15 { 16 memcpy(d, g, sizeof g); 17 for(int k = 0 ; k < n ; k ++) 18 for(int i = 0 ; i < n ; i ++) 19 for(int j = 0 ; j < n ; j ++) 20 d[i][j] |= d[i][k] && d[k][j]; 21 } 22 23 int check() 24 { 25 for(int i = 0 ; i < n ; i ++)//第二种情况 26 if(d[i][i])return 2; 27 28 for(int i = 0 ; i < n ; i ++) 29 for(int j = 0 ; j < i ; j ++) 30 if(!d[i][j] && !d[j][i])return 0;//第一种情况,由于对称性,j只需要枚举到i - 1 31 return 1;//第三种情况 32 } 33 34 35 int get_min() 36 { 37 for(int i = 0 ; i < n ; i ++) 38 if(!st[i]) 39 { 40 bool flag = true; 41 for(int j = 0 ; j < n ; j ++) 42 if(!st[j] && d[j][i])//在i这个数前面的未被选用的数存在,那么i就不是最小 43 { 44 flag = false; 45 break; 46 } 47 if(flag)//i是最小,就标记,输出 48 { 49 st[i] = true; 50 return 'A' + i; 51 } 52 } 53 } 54 55 int main(){ 56 while(cin >> n >> m, n || m) 57 { 58 memset(g, 0, sizeof g); 59 int type = 0, t; 60 for(int i = 1 ; i <= m ; i ++) 61 { 62 char str[5]; 63 64 cin >> str; 65 66 int a = str[0] - 'A', b = str[2] - 'A'; 67 68 if(!type) 69 { 70 g[a][b] = 1; 71 Floyd(); 72 type = check(); 73 if(type)t = i; 74 } 75 76 } 77 78 if(!type)puts("Sorted sequence cannot be determined."); 79 else if(type == 2)printf("Inconsistency found after %d relations.\n", t); 80 else 81 { 82 memset(st, 0, sizeof st); 83 printf("Sorted sequence determined after %d relations: ", t); 84 for(int i = 0 ; i < n ; i ++)printf("%c", get_min()); 85 printf(".\n"); 86 } 87 } 88 return 0; 89 }
也可以根据传递的性质,将其优化掉一维的循环。
1 #include <iostream> 2 #include <algorithm> 3 #include <cstring> 4 5 using namespace std; 6 7 const int N = 30; 8 int d[N][N]; 9 bool st[N]; 10 int n, m; 11 12 13 int check() 14 { 15 for(int i = 0 ; i < n ; i ++) 16 if(d[i][i])return 2;//矛盾 17 for(int i = 0 ; i < n ; i ++) 18 for(int j = 0 ; j < i ; j ++) 19 if(!d[i][j] && !d[j][i]) 20 return 0;//不确定 21 return 1; 22 } 23 24 int get_min() 25 { 26 for(int i = 0 ; i < n ; i ++) 27 if(!st[i]) 28 { 29 bool flag = true; 30 for(int j = 0 ; j < n ; j ++) 31 if(!st[j] && d[j][i]) 32 { 33 flag = false; 34 break; 35 } 36 if(flag) 37 { 38 st[i] = true; 39 return 'A' + i; 40 } 41 } 42 } 43 44 int main(){ 45 while(cin >> n >> m, n || m) 46 { 47 48 int type = 0, t; 49 memset(d, 0, sizeof d); 50 for(int i = 1 ; i <= m ; i ++) 51 { 52 char str[5]; 53 cin >> str; 54 int a = str[0] - 'A', b = str[2] - 'A'; 55 if(!type) 56 { 57 d[a][b] = 1; 58 for(int x = 0 ; x < n ; x ++) 59 { 60 if(d[x][a])d[x][b] = 1; 61 if(d[b][x])d[a][x] = 1; 62 for(int y = 0 ; y < n ; y ++) 63 { 64 if(d[x][a] && d[b][y]) 65 d[x][y] = 1; 66 } 67 } 68 type = check(); 69 if(type)t = i; 70 } 71 } 72 if(!type)puts("Sorted sequence cannot be determined."); 73 else if(type == 2)printf("Inconsistency found after %d relations.\n", t); 74 else 75 { 76 memset(st, 0, sizeof st); 77 printf("Sorted sequence determined after %d relations: ", t); 78 for(int i = 0 ; i < n ; i ++)printf("%c", get_min()); 79 printf(".\n"); 80 } 81 } 82 return 0; 83 }
无向图的最小环。
https://www.acwing.com/problem/content/346/
acwing 344.观光之旅
当外层循环$k$刚开始的时候,$d[i][j]$保存着经过编号不超过$k - 1$的节点,从$i$到$j$的最短路长度。因此,对于经过编号不超过$k$的节点,从i到j的最小环长度就是$d[i][j] + a[i][k] + a[k][j]$。
关于路径的存储。$Floyd$算法的路径更新的条件为$d[i][j] >= d[i][k] + d[k][j]$。如果$d[i][j]$能转移过来,那么说明$d[i][j] = d[i][k] + d[k][j]$。中间点是$k$。这样就由$k$为分界点,将点分为了$0-k$,$k-n$的两段,递归到这两段中,可以继续求得转移的路径。并且转移的状态都是不重复的。
1 #include <iostream> 2 #include <algorithm> 3 #include <cstring> 4 5 using namespace std; 6 7 const int N = 110, M = 20010; 8 int d[N][N],g[N][N]; 9 int pos[N][N],path[N]; 10 int cnt; 11 12 void get_path(int i, int j) 13 { 14 if(pos[i][j] == 0)return ; 15 16 int k = pos[i][j]; 17 get_path(i, k); 18 path[cnt ++] = k; 19 get_path(k, j); 20 } 21 22 int main(){ 23 int n, m; 24 cin >> n >> m; 25 26 memset(g, 0x3f, sizeof g); 27 for(int i = 1 ; i <= n ; i ++)g[i][i] = 0; 28 29 while(m --) 30 { 31 int a, b, c; 32 cin >> a >> b >> c; 33 g[a][b] = g[b][a] = min(g[a][b], c); 34 } 35 36 int res = 0x3f3f3f3f; 37 memcpy(d, g, sizeof d); 38 for(int k = 1 ; k <= n ; k ++) 39 { 40 for(int i = 1 ; i < k ; i ++) 41 for(int j = i + 1 ; j < k ; j ++) 42 if((long long)d[i][j] + g[i][k] + g[k][j] < res) 43 { 44 res = d[i][j] + g[i][k] + g[k][j]; 45 cnt = 0; 46 path[cnt ++] = k; 47 path[cnt ++] = i; 48 get_path(i, j); 49 path[cnt ++] = j;//对于环,逆时针,点依次是k, i, i~j之间的点(递归求得), j 50 } 51 for(int i = 1 ; i <= n ; i ++) 52 for(int j = 1 ; j <= n ; j ++) 53 { 54 if(d[i][j] > d[i][k] + d[k][j]) 55 { 56 d[i][j] = d[i][k] + d[k][j]; 57 pos[i][j] = k; 58 } 59 } 60 } 61 62 if(res == 0x3f3f3f3f)puts("No solution."); 63 else for(int i = 0 ; i < cnt ; i ++)cout << path[i] << ' '; 64 65 return 0; 66 }

浙公网安备 33010602011771号