演算法 圖中求最小環路徑 最小環個數 最大平均環 求簡單無向圖中環的個數
阿新 • • 發佈:2019-02-03
最小環問題:求個圖中環路徑代價最小的迴路。
如何求最小環?假如有 路徑1->3->2,如果此時已經知道2-1的最短路徑就好了。 回想下floyed的更新過程,就會發現更新第k次時,比k小的點之間都是最短距離的(要是點是聯通的話)。所以給出解法:第k次更新圖時,列舉和k相連的兩條邊。如 環路代價 = dist[i][k] + dist[k][j] + dist[j][i];
求無向圖中最小環的個數,先算出一個最小環代價,把之後算出的環代價與之對比更新就可以了。這個圖中是求不同的最小環的個數,所以重邊是沒有影響的
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; #define maxn 108 #define INF 1<<24 int dist[maxn][maxn], g[maxn][maxn]; int n, m; void init(){ for ( int i=1; i<=n; ++i ) for ( int j=1; j<=n; ++j ) dist[i][j] = g[i][j] = INF; int u, v, w; for ( int i=0; i<m; ++i ) { scanf("%d%d%d", &u, &v, &w); if(dist[u][v] > w) { dist[u][v] = dist[v][u] = w; g[u][v] = g[v][u] = w; } } }; void solve(){ int minn = INF, cnt=0; for ( int k=1; k<=n; ++k ){ // 列舉與k相連的兩條邊,且端點號是小於k的 for ( int i=1; i<k; ++i ) if(g[i][k]^INF) for( int j=i+1; j<k; ++j ) if(dist[i][j]^INF && g[k][j]^INF) { int tmp = dist[i][j]+g[i][k]+g[k][j]; if(tmp < minn ) {// 比最小的還小就更新 minn = tmp; cnt=1; }else if(tmp == minn) ++cnt; // 和當前最小的環代價相等 } // 更新最短路 for(int i=1; i<=n; ++i ) if(dist[i][k]^INF) for(int j=1; j<=n; ++j ) if(dist[k][j]^INF) { int tmp= dist[i][k]+dist[k][j]; if(tmp < dist[i][j]) dist[i][j] = tmp; } } if(cnt > 0) printf("%d %d\n", minn, cnt); else puts("-1 -1"); }; int main(){ int T; scanf("%d", &T); while( T-- ) { scanf("%d%d", &n, &m); init(); solve(); } };
求最小環路徑,任意一條最小環的路徑。
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <vector> #include <algorithm> using namespace std; #define maxn 108 int n, m; int dist[maxn][maxn], g[maxn][maxn]; int pre[maxn][maxn]; // 路徑 #define INF 1<<24 vector<int > ans; void init(){ for(int i=1; i<=n; ++i) for(int j=1; j<=n; ++j) dist[i][j]=g[i][j]=INF, pre[i][j]=i; int u, v, w; for(int i=0; i<m; ++i ) { scanf("%d%d%d", &u, &v, &w); if(w < dist[u][v] ){ dist[u][v] = dist[v][u] = w; g[u][v] = g[v][u] = w; } } }; void solve(){ int minn = INF; ans.clear(); for(int k=1; k<=n; ++k ) { // 列舉兩條邊,點號小於k for(int i=1; i<k; ++i) if(g[i][k]^INF) for(int j=i+1; j<k; ++j) if(g[k][j]^INF && dist[i][j]^INF) { int tmp = dist[i][j]+g[i][k]+g[k][j]; if(tmp < minn) { // 求出路徑 minn = tmp; ans.clear(); int p = j; while(p != i) { ans.push_back(p); p = pre[i][p]; } ans.push_back(i); ans.push_back(k); } } for(int i=1; i<=n; ++i ) if(dist[i][k]^INF) for(int j=1; j<=n; ++j )if(dist[k][j]^INF) { int tmp = dist[i][k]+dist[k][j]; if(tmp < dist[i][j]) { dist[i][j]= tmp; pre[i][j] = pre[k][j]; // 從後往前 } } } if(minn ^ INF) { //cout<< minn << endl; for(int i=0; i<ans.size(); ++i){ if(i) printf(" "); printf("%d", ans[i]); } puts(""); return ; } puts("No solution."); }; int main(){ while(~scanf("%d%d", &n, &m)){ init(); solve(); } };
最大環路問題和最小環路是相同的吧(沒遇到過求最大環的);
求最大平均環代價。 知道spfa能夠判斷環中是否環(正環和負環都是可以的)。開始圖中的都是正環(假如有環的話)。對每條邊減去一個值,再判斷圖中是否有正環存在,恰好沒有就表明和這個值就是最大的平均環的代價,因為環中x條邊都減去y後,恰好會使得整個環不是正環,這個y值就是要求的最大平均環代價了;
求解時只需二分列舉y即可,注意精度;
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int maxm = 100005; const int maxn = 27*27; int n, head[maxn], e, N; struct Edge{ int u, v, next; double w; Edge(){} Edge(int U, int V, int Ne, double W): u(U), v(V), next(Ne), w(W){} }edge[maxm]; void add(int u, int v, double w){ edge[e] = Edge(u, v, head[u], w); head[u] = e++; } int mm[30][30]; int get( char x, char y) { int i = x-'a', j = y-'a'; if(mm[i][j] == 0) mm[i][j]=++N; return mm[i][j]; } double maxLength; void init(){ char str[1071]; e = 0; N = 0; maxLength = 0; //maxLength 最大的邊 memset(head, -1, sizeof head); //開始這裡我用的是fill,悲劇的rt瞭如幹次啊 memset(mm, 0, sizeof mm); getchar(); for(int i=0; i<n; ++i ) { scanf("%s", str); int sz = strlen(str); if(sz < 3) continue; int u = get(str[0], str[1]); int v = get(str[sz-2], str[sz-1]); add(u, v, sz); //每個字串見條邊就可以了 if(sz > maxLength) maxLength = sz; } } double dist[maxn]; int cnt[maxn]; bool vis[maxn]; int Q[maxn]; bool spfa( double x ) { fill(dist+1, dist+1+N, 0); fill(cnt+1, cnt+1+N, 0 ); memset(vis, 0, sizeof vis); int l=0, r=0; for(int i=1; i<=N; ++i) Q[r++] = i, vis[i]=1; while(l != r) { int u = Q[l++]; if(l == maxn) l=0; vis[u]=0; for ( int i=head[u]; ~i; i=edge[i].next){ int v = edge[i].v; double w = edge[i].w; if(dist[u]+w-x > dist[v]){ dist[v] = dist[u]+w-x; if(vis[v] ) continue; Q[r++] = v; vis[v]=1; if(r == maxn) r=0; if(++cnt[v] > N) return 1; // 存在正環 } } } return 0; } #define eps 1e-4 void solve() { double l = 0, r = maxLength, mid, ans=-1; while(r-l >= eps){// 二分球結果 mid = (l+r)/2.0; if(spfa( mid ) ) { ans = mid; l = mid; }else r = mid; } if(ans > eps) printf("%.2lf\n", ans); else puts("No solution."); }; int main(){ while( scanf("%d", &n), n ){ init(); solve(); } }
以上~~
轉載於:http://www.cnblogs.com/TengXunGuanFangBlog/archive/2013/04/19/loop_problem.html