题目链接:http://codeforces.com/gym/100917/problem/F
------------------------------------------------------------------------------------------------
给出一个无向正权无自环图 要求对于每个点 经过它的最短"简单环"的长度
其中简单环的定义是 环上每条无向边都经过且仅经过一次
显然这个环至少经过三条边 三个点
我们或许会产生这样一种思路 对于每次询问 我们以该点$s$作为起点
先处理出到其余每点的最短路 然后枚举一条边 两端分别为$u\ v$
用$s$分别到$u\ v$的最短路再加上这条边的长度来更新结果
然而这样连样例都过不了 因为会产生重边
于是我们考虑把最短路径所经过的边都标记一下(多个可选的话随便标记一个)
然后枚举边的时候碰到标记过的边就直接跳过
不过这样还存在一种情况 就是 $u\ v$ 属于最短路径边所构成树上的除根节点外同一子树
这种情况也是会有重边的 所以这里再判断以下去除就好
1 #include <cstdio> 2 #include <cstring> 3 #include <cmath> 4 #include <algorithm> 5 using namespace std; 6 const int N = 310, E = N * N * 2; 7 int firste[N], nexte[E], v[E], w[E], flag[E]; 8 int used[N], dist[N], fa[N]; 9 int n, e = 1; 10 void build(int x, int y, int z) 11 { 12 nexte[++e] = firste[x]; 13 firste[x] = e; 14 v[e] = y; 15 w[e] = z; 16 } 17 int main() 18 { 19 scanf("%d", &n); 20 int x; 21 for(int i = 1; i <= n; ++i) 22 for(int j = 1; j <= n; ++j) 23 { 24 scanf("%d", &x); 25 if(i >= j) 26 continue; 27 if(x != -1) 28 { 29 build(i, j, x); 30 build(j, i, x); 31 } 32 } 33 for(int i = 1; i <= n; ++i) 34 { 35 int u = i; 36 for(int j = 1; j <= n; ++j) 37 fa[j] = j; 38 memset(dist, 0x3f, sizeof dist); 39 dist[u] = 0; 40 for(int t = 1; t < n; ++t) 41 { 42 used[u] = i; 43 int mdist = 1e9, tu; 44 for(int p = firste[u]; p; p = nexte[p]) 45 if(dist[v[p]] > dist[u] + w[p]) 46 dist[v[p]] = dist[u] + w[p]; 47 for(int j = 1; j <= n; ++j) 48 if(used[j] != i && dist[j] < mdist) 49 { 50 tu = j; 51 mdist = dist[j]; 52 } 53 for(int p = firste[tu]; p; p = nexte[p]) 54 if(dist[tu] == dist[v[p]] + w[p]) 55 { 56 flag[p] = flag[p ^ 1] = i; 57 if(v[p] != i) 58 fa[tu] = fa[v[p]]; 59 break; 60 } 61 u = tu; 62 } 63 int ans = 1e9; 64 for(int p = 2; p < e; p +=2) 65 if(flag[p] != i && fa[v[p]] != fa[v[p ^ 1]]) 66 ans = min(ans, dist[v[p]] + dist[v[p ^ 1]] + w[p]); 67 printf("%d\n", ans < 1e9 ? ans : -1); 68 } 69 return 0; 70 }
时间: 2024-10-19 17:32:21