算法 图中求最小环路径 最小环个数 最大平均环 求简单无向图中环的个数

来源:互联网 发布:qq网络硬盘在哪里找 编辑:程序博客网 时间:2024/06/03 05:03

最小环问题:求个图中环路径代价最小的回路。

如何求最小环?假如有 路径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<<2410 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 10810 int n, m;11 int dist[maxn][maxn], g[maxn][maxn];12 int pre[maxn][maxn]; // 路径13 #define INF 1<<2414 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        // 枚举两条边,点号小于k36         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-478 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 };
复制代码

 

0 0
原创粉丝点击