POJ 1679 The Unique MST

POJ 1679 The Unique MST 

 

关于次小生成树的构造:

 

在求最小生成树时,用数组Max[i][j]来表示MST中i到j最大边权。

求完后,直接枚举所有不在MST中的边,把它加入到MST中构成一棵新的树,且该树有环,此环是由刚加入的边(i,j)造成的,所以可以通过删除Max[i][j]即可得到新的一颗树,且所有的该类树中必有一棵为次小生成树。

如图所示:

G,H不是MST上的边,通过加入边(G,H),得到一个环(B,H,G),由于在计算最小生成树时已经计算出G,H之间最大边权为Max[G][H] = BH,所以通过删除BH即可得到一棵此时最小的生成树,然后更新答案即可。

实际上,我们知道MST的构造是具有贪心性质的,假如上图是一棵MST的话,那么必然有w(B,H) < w(G,H)。实际上w(B,H)是G->H的第二大边权,所以我们可以增加w(G,H),然后删除w(B,H)得到的是权值和第二大的树,这时我们就可以通过枚举所有不在MST树中(!use[i][j])的办法来生成次小生成树。

 

CODE:

 

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
using  namespace std;

const  int MAXN =  110;
const  int INF =  0x3f3f3f3f;
int N, M;
int w[MAXN][MAXN] ,d[MAXN];
bool vis[MAXN], use[MAXN][MAXN];
int cnt ,fa[MAXN], Max[MAXN][MAXN] ;

void init()
{
    cnt =  0;
    memset(use,  0sizeof(use));
    memset(vis,  0sizeof(vis));
    memset(w, INF,  sizeof(w));
    memset(Max,  0sizeof(Max));
}

void Prim( int src)
{
     for( int i =  1; i <= N; i++) d[i] = (i == src)?  0:INF;
     for( int i =  1; i <= N; i++) fa[i] = i;
     for( int i =  1; i <= N; i++)
    {
         int m = INF, x ;
         for( int y =  1; y <= N; y++)  if(!vis[y] && d[y] < m) m = d[x=y];
         for( int y =  1; y <= N; y++)  if(vis[y]) /* 这是算法的关键,记录下vis[y]到k的路径中权值最大的值,用于替换处理 */
        {
            Max[x][y] = Max[y][x] = max(Max[y][fa[x]], d[x]);     // 这里我出现了一个非常严重的错误,POJ竟然没报错!!
            
// Max[x][y] = Max[y][x] = d[x]; 
        }
        vis[x] =  1;
        use[x][fa[x]] = use[fa[x]][x] =  1// 标记在MST树中 
        cnt += m;
         for( int y =  1; y <= N; y++)
        {
             if(!vis[y] && d[y] > w[x][y])
            {
                d[y] = w[x][y];
                fa[y] = x;  // 记录父亲节点 
             }
        }
    }
}

int smst()  // second MST
{
     int Min = INF;
     for( int i =  1; i <= N; i++)
    {
         for( int j = i+ 1; j <= N; j++)
        {
             if(w[i][j] != INF && !use[i][j])
            {
                 int res = cnt + w[i][j] - Max[i][j];
                Min = min(Min, res);
            }
        }
    }
     if(Min == cnt)  return  0;
     return  1;
}


int main()
{
     int T;
    scanf( " %d ", &T);
     while(T--)
    {
        init();
        scanf( " %d%d ", &N, &M);
         while(M--)
        {
             int u, v, cost;
            scanf( " %d%d%d ", &u, &v, &cost);
             if(w[u][v] > cost)  // 可能有重边 
            {
                w[u][v] = w[v][u] = cost;
            }
        }
        Prim( 1);
         if(smst())
        {
            printf( " %d\n ", cnt);
        }
         else printf( " Not Unique!\n ");
    }
     return  0;
}

 

 CODE:

 

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
using  namespace std;

const  int MAXN =  110;
const  int INF =  0x3f3f3f3f;
int n, m;
int w[MAXN][MAXN], d[MAXN];
bool vis[MAXN], use[MAXN][MAXN];
int cnt ,fa[MAXN], path[MAXN][MAXN];

void init()
{
    cnt =  0;
    memset(use,  0sizeof(use));
    memset(vis,  0sizeof(vis));
    memset(w, INF,  sizeof(w));
    memset(path,  0sizeof(path));
}

void Prim( int src)
{
     for( int i =  1; i <= n; i++) { d[i] = (i == src)? 0:INF; fa[i] = i; }
     for( int i =  1; i <= n; i++)
    {
         int x, m = INF;
         for( int y =  1; y <= n; y++)  if(!vis[y] && m > d[y]) m = d[x=y];
         for( int y =  1; y <= n; y++)  if(vis[y])  // 更新 
        {
            path[x][y] = path[y][x] = max(path[y][fa[x]], d[x]);
        }
        vis[x] =  1;
        use[x][fa[x]] = use[fa[x]][x] =  1;
        cnt += m;
         for( int y =  1; y <= n; y++)  if(d[y] > w[x][y])
        {
            d[y] = w[x][y];
            fa[y] = x;
        }
    }
}

int smst()
{
     int Min = INF, res;
     for( int i =  1; i <= n; i++)
    {
         for( int j = i+ 1; j <= n; j++)  if(w[i][j] != INF && !use[i][j])  // 枚举 
        {
            res = cnt + w[i][j] - path[i][j];
            Min = min(Min, res);
        }
    }
     if(Min == cnt)  return  0;
     return  1;
}


int main()
{
     int T;
    scanf( " %d ", &T);
     while(T--)
    {
        init();
        scanf( " %d%d ", &n, &m);
         while(m--)
        {
             int u, v, cost;
            scanf( " %d%d%d ", &u, &v, &cost);
             if(w[u][v] > cost)  // 可能有重边 
            {
                w[u][v] = w[v][u] = cost;
            }
        }
        Prim( 1);
         if(smst())
        {
            printf( " %d\n ", cnt);
        }
         else printf( " Not Unique!\n ");
    }
     return  0;
}

 

你可能感兴趣的:(unique)