最小环问题:求个图中环路径代价最小的回路。
如何求最小环?假如有 路径1->3->2,如果此时已经知道2-1的最短路径就好了。 回想下floyed的更新过程,就会发现更新第k次时,比k小的点之间都是最短距离的(要是点是联通的话)。所以给出解法:第k次更新图时,枚举和k相连的两条边。如 环路代价 = dist[i][k] + dist[k][j] + dist[j][i];
求无向图中最小环的个数,先算出一个最小环代价,把之后算出的环代价与之对比更新就可以了。这个图中是求不同的最小环的个数,所以重边是没有影响的
fzu 2090 http://acm.fzu.edu.cn/problem.php?pid=2090
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 using namespace std; 7 8 #define maxn 108 9 #define INF 1<<24 10 int dist[maxn][maxn], g[maxn][maxn]; 11 int n, m; 12 13 void init(){ 14 for ( int i=1; i<=n; ++i ) 15 for ( int j=1; j<=n; ++j ) 16 dist[i][j] = g[i][j] = INF; 17 int u, v, w; 18 for ( int i=0; i<m; ++i ) { 19 scanf("%d%d%d", &u, &v, &w); 20 if(dist[u][v] > w) { 21 dist[u][v] = dist[v][u] = w; 22 g[u][v] = g[v][u] = w; 23 } 24 } 25 }; 26 27 void solve(){ 28 int minn = INF, cnt=0; 29 for ( int k=1; k<=n; ++k ){ 30 // 枚举与k相连的两条边,且端点号是小于k的 31 for ( int i=1; i<k; ++i ) if(g[i][k]^INF) 32 for( int j=i+1; j<k; ++j ) if(dist[i][j]^INF && g[k][j]^INF) 33 { 34 int tmp = dist[i][j]+g[i][k]+g[k][j]; 35 if(tmp < minn ) {// 比最小的还小就更新 36 minn = tmp; cnt=1; 37 }else if(tmp == minn) ++cnt; // 和当前最小的环代价相等 38 } 39 40 // 更新最短路 41 for(int i=1; i<=n; ++i ) if(dist[i][k]^INF) 42 for(int j=1; j<=n; ++j ) if(dist[k][j]^INF) 43 { 44 int tmp= dist[i][k]+dist[k][j]; 45 if(tmp < dist[i][j]) dist[i][j] = tmp; 46 } 47 } 48 if(cnt > 0) printf("%d %d\n", minn, cnt); 49 else puts("-1 -1"); 50 }; 51 52 int main(){ 53 int T; scanf("%d", &T); 54 while( T-- ) { 55 scanf("%d%d", &n, &m); 56 init(); 57 solve(); 58 } 59 };
求最小环路径,任意一条最小环的路径。
poj 1734 http://poj.org/problem?id=1734
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <vector> 6 #include <algorithm> 7 using namespace std; 8 9 #define maxn 108 10 int n, m; 11 int dist[maxn][maxn], g[maxn][maxn]; 12 int pre[maxn][maxn]; // 路径 13 #define INF 1<<24 14 vector<int > ans; 15 16 void init(){ 17 for(int i=1; i<=n; ++i) 18 for(int j=1; j<=n; ++j) 19 dist[i][j]=g[i][j]=INF, pre[i][j]=i; 20 int u, v, w; 21 for(int i=0; i<m; ++i ) { 22 scanf("%d%d%d", &u, &v, &w); 23 if(w < dist[u][v] ){ 24 dist[u][v] = dist[v][u] = w; 25 g[u][v] = g[v][u] = w; 26 } 27 } 28 }; 29 30 31 void solve(){ 32 int minn = INF; ans.clear(); 33 for(int k=1; k<=n; ++k ) 34 { 35 // 枚举两条边,点号小于k 36 for(int i=1; i<k; ++i) if(g[i][k]^INF) 37 for(int j=i+1; j<k; ++j) if(g[k][j]^INF && dist[i][j]^INF) { 38 int tmp = dist[i][j]+g[i][k]+g[k][j]; 39 if(tmp < minn) { // 求出路径 40 minn = tmp; ans.clear(); 41 int p = j; 42 while(p != i) { 43 ans.push_back(p); 44 p = pre[i][p]; 45 } 46 ans.push_back(i); 47 ans.push_back(k); 48 } 49 } 50 51 for(int i=1; i<=n; ++i ) if(dist[i][k]^INF) 52 for(int j=1; j<=n; ++j )if(dist[k][j]^INF) { 53 int tmp = dist[i][k]+dist[k][j]; 54 if(tmp < dist[i][j]) { 55 dist[i][j]= tmp; 56 pre[i][j] = pre[k][j]; // 从后往前 57 } 58 } 59 } 60 61 if(minn ^ INF) { 62 //cout<< minn << endl; 63 for(int i=0; i<ans.size(); ++i){ 64 if(i) printf(" "); printf("%d", ans[i]); 65 } 66 puts(""); 67 return ; 68 } 69 puts("No solution."); 70 }; 71 72 int main(){ 73 while(~scanf("%d%d", &n, &m)){ 74 init(); 75 solve(); 76 } 77 };
最大环路问题和最小环路是相同的吧(没遇到过求最大环的);
求最大平均环代价。 知道spfa能够判断环中是否环(正环和负环都是可以的)。开始图中的都是正环(假如有环的话)。对每条边减去一个值,再判断图中是否有正环存在,恰好没有就表明和这个值就是最大的平均环的代价,因为环中x条边都减去y后,恰好会使得整个环不是正环,这个y值就是要求的最大平均环代价了;
poj 2949 http://poj.org/problem?id=2949
求解时只需二分枚举y即可,注意精度;
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 7 const int maxm = 100005; 8 const int maxn = 27*27; 9 int n, head[maxn], e, N; 10 struct Edge{ 11 int u, v, next; double w; 12 Edge(){} 13 Edge(int U, int V, int Ne, double W): 14 u(U), v(V), next(Ne), w(W){} 15 }edge[maxm]; 16 17 void add(int u, int v, double w){ 18 edge[e] = Edge(u, v, head[u], w); head[u] = e++; 19 } 20 21 int mm[30][30]; 22 23 int get( char x, char y) { 24 int i = x-'a', j = y-'a'; 25 if(mm[i][j] == 0) mm[i][j]=++N; 26 return mm[i][j]; 27 } 28 29 double maxLength; 30 31 void init(){ 32 char str[1071]; 33 e = 0; N = 0; maxLength = 0; //maxLength 最大的边 34 memset(head, -1, sizeof head); //开始这里我用的是fill,悲剧的rt了如干次啊 35 memset(mm, 0, sizeof mm); 36 37 getchar(); 38 for(int i=0; i<n; ++i ) { 39 scanf("%s", str); 40 int sz = strlen(str); 41 if(sz < 3) continue; 42 int u = get(str[0], str[1]); 43 int v = get(str[sz-2], str[sz-1]); 44 add(u, v, sz); //每个字串见条边就可以了 45 if(sz > maxLength) maxLength = sz; 46 } 47 } 48 49 double dist[maxn]; 50 int cnt[maxn]; 51 bool vis[maxn]; 52 int Q[maxn]; 53 54 bool spfa( double x ) { 55 fill(dist+1, dist+1+N, 0); 56 fill(cnt+1, cnt+1+N, 0 ); 57 memset(vis, 0, sizeof vis); 58 int l=0, r=0; 59 for(int i=1; i<=N; ++i) 60 Q[r++] = i, vis[i]=1; 61 while(l != r) { 62 int u = Q[l++]; if(l == maxn) l=0; vis[u]=0; 63 for ( int i=head[u]; ~i; i=edge[i].next){ 64 int v = edge[i].v; double w = edge[i].w; 65 if(dist[u]+w-x > dist[v]){ 66 dist[v] = dist[u]+w-x; 67 if(vis[v] ) continue; 68 Q[r++] = v; vis[v]=1; 69 if(r == maxn) r=0; 70 if(++cnt[v] > N) return 1; // 存在正环 71 } 72 } 73 } 74 return 0; 75 } 76 77 #define eps 1e-4 78 void solve() 79 { 80 double l = 0, r = maxLength, mid, ans=-1; 81 while(r-l >= eps){// 二分球结果 82 mid = (l+r)/2.0; 83 if(spfa( mid ) ) { 84 ans = mid; 85 l = mid; 86 }else r = mid; 87 } 88 if(ans > eps) printf("%.2lf\n", ans); 89 else puts("No solution."); 90 }; 91 92 93 int main(){ 94 while( scanf("%d", &n), n ){ 95 init(); 96 solve(); 97 } 98 }
求简单无向图中环的个数?简单无向图即 无自环,无重边的无向图。
这个是NP问题; codeforces 上有一题用的是状态压缩写的。
dp[s][i] s集合里最小的点到其他点的路径数;
dp[s][i] += dp[s^(1<<i)][j](g[j][i]=true)
ans加上可以构成环的路径数.怎么才能构成环呢? 如a->b->.....->c ,如果知道ac是可达的,只要加上a,经过ab...到达c的路径数就可以了。注意a是这个集合里最小的数。而且同一个环会被记录两次,因为2条路径才是一个环。
codeforces 11D http://www.codeforces.com/contest/11/problem/D
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 using namespace std; 6 7 typedef __int64 ll; 8 #define maxn 20 9 ll dp[1<<maxn][maxn]; // 注意数据 10 bool g[maxn][maxn]; 11 // dp[s][i] s中最小的点到其他点路径数; 12 // dp[s][i] += dp[s^i][j](g[i][j] = true ) 13 14 int main(){ 15 int n, m; 16 while( cin >> n >> m ) { 17 memset(g, 0, sizeof g); 18 int state = (1<<n); 19 for ( int i=0; i<state; ++i) 20 for ( int j=0; j<n; ++j ) 21 dp[i][j] = 0; 22 23 for ( int i=0, a, b; i<m; ++i ){ 24 scanf ("%d%d", &a, &b); 25 --a, --b; 26 g[a][b] = g[b][a] = true; 27 dp[(1<<a)|(1<<b)][a] = dp[(1<<a)|(1<<b)][b] = 1; 28 } 29 30 ll ans = 0; 31 for ( int s=1; s<state; ++s ){ 32 int i, j, k; 33 for ( i=0; i<n && !(s&(1<<i)); ++i ); 34 for ( j=i+1; j<n; ++j ) if(s&(1<<j) ) 35 { 36 for ( k=i+1; k<n; ++k ) if(s&(1<<k)){ 37 if(g[k][j] ) 38 dp[s][j] += dp[s^(1<<j)][k]; 39 } 40 if(g[i][j] && (s^(1<<i)^(1<<j))) // 3个点以上才行 41 ans += dp[s][j]; 42 } 43 } 44 // 枚举了环的两侧,so。。。 45 cout << (ans>>1) << endl; 46 } 47 };