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


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

    如何求最小环?假如有 路径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

    View Code
     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

    View Code
     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即可,注意精度;

    View Code
     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

    View Code
     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 };
  • 相关阅读:
    并查集 示例 : poj 1703 [Find them, Catch them 帮派之争]
    《Python 第五章》条件,循环和其他语句
    《Python 第一章》基础知识
    heapSort 堆排序 / 二叉堆
    并查集 hdu 1856 示例
    KMP / hdu 1711 [找到匹配的位置并返回]
    《Python 第四章》字典
    EnumChildWindows的使用
    C#查找指定窗口的子窗口的句柄
    C#里字符串取左边N个字符,右边N个字符,从中间取N个字符的函数
  • 原文地址:https://www.cnblogs.com/TengXunGuanFangBlog/p/loop_problem.html
Copyright © 2020-2023  润新知