1. 程式人生 > >演算法 圖中求最小環路徑 最小環個數 最大平均環 求簡單無向圖中環的個數

演算法 圖中求最小環路徑 最小環個數 最大平均環 求簡單無向圖中環的個數

最小環問題:求個圖中環路徑代價最小的迴路。

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