POJ - 1679 The Unique MST (次小生成树)

题意:就是判断图的最小生成树是否唯一,即求其最小生成树(MST)和次小生成树。

虽然是一道模板题,更重要的是理解求次小生成树的过程。求次小生成树建立在Prim算法的基础上。可以确定的是,次小生成树肯定是由最小生成树删去一条边再加上一条边得到。那么我们应该删去哪条边再加上哪条边呢?假设两点u,v之间有一条边且这条边不在MST中,那么可以尝试加上这条边。但是加上这条边以后会出现环,则一定要去掉回路上的一条边,这条边应该选择回路上权值最大的那条边(毕竟权值要求尽量小)。尝试对每对点对进行上述操作,那么最小的那个结果就是次小生成树。

算法中用一个二维数组path[u][v]维护已经确定的最小生成树上的每对结点之间的路径上,权值最大的那条边,而这个维护的过程就是在Prim算法中增加的部分。

当求出最小生成树以后,我们也得到了完整的path数组。然后枚举任意两个点对u和v,求出min(MST-path[u][v]+G[u][v]),结果就是次小生成树的值。

邻接矩阵实现:

#include<iostream>
#include<stdio.h>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn =1e2+5;
const int INF = 0x3f3f3f3f;
int G[maxn][maxn],dist[maxn];
int path[maxn][maxn];       //记录生成树上每对结点得最大边权
bool used[maxn][maxn];
int pre[maxn];
bool vis[maxn];
struct Edge{
    int to,val;
};

void init(int N)
{
    for(int i=0;i<=N;++i){
        for(int j=i;j<=N;++j){
            G[i][j]=G[j][i]=INF;
        }
    }
}

void AddEdge(int u,int v,int val)
{
    G[v][u]=G[u][v]=val;
}


int prim(int N)
{
    int Mst=0;
    memset(used,0,sizeof(used));
    memset(path,0,sizeof(path));
    memset(vis,0,sizeof(vis));
    vis[1]=true;                    //从1开始
    
    for(int i=1; i<=N; ++i){
        dist[i]=G[1][i];
        pre[i]=1;
    }

    for(int i=1;i<N;++i){
        int u=-1;
        for(int j=1;j<=N;++j){
            if(!vis[j] && (u==-1 || dist[j]<dist[u]))
                u=j;
        }        
        used[u][pre[u]]= used[pre[u]][u]=true;
        vis[u]=true;
        Mst+=G[pre[u]][u];
        for(int j=1;j<=N;++j){
            if(vis[j] && j!=u){     //若j已经被访问且不是u本身,更新点对u和j的path
                path[u][j]=path[j][u]=max(path[j][pre[u]],dist[u]); 
            }
            else if(!vis[j]){       //若j没被访问,更新dist
                if(dist[j]>G[u][j]){
                    dist[j]=G[u][j];
                    pre[j]=u;
                }
            }
        }
    }
    return Mst;
}

int main()
{
    int T,N,M,u,v,tmp;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&N,&M);
        init(N);
        for(int i=0;i<M;++i){
            scanf("%d%d%d",&u,&v,&tmp);
            AddEdge(u,v,tmp);
            AddEdge(v,u,tmp);
        }
        int Mst=prim(N);
        int res=INF;
        for(int i=1; i<=N; ++i){
            for(int j=1; j<=N; ++j){
                if(i!=j && !used[i][j]){
                    res=min(res,Mst+G[i][j]-path[i][j]);        //检查每对结点
                }
            }
        }

        if(res==Mst)printf("Not Unique!\n");
        else    printf("%d\n",Mst);
    }
    return 0;
}

 

posted @ 2018-07-22 11:42  xiuwenL  阅读(125)  评论(0编辑  收藏  举报