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

题意:求解最小生成树的权值是否唯一,即要我们求次小生成树的权值
两种方法求最小生成树,一种用prim算法, 一种用kruskal算法

 

一:用prim算法

对于给定的图,我们可以证明,次小生成树可以由最小生成树变换一边得到。 那么我们可以如下求给定图的次小生成树。首先,我们用prime算法求出图的最小生成树, 在这个过程中记录每条边是否用过,以及两个点之间最短路径上的最大权值F[i,j]

F[i,j]可以如此求得,当加入点u的时候,并且u的父结点是v 那么对于已经在生成树中的节点x F[x,u] = max(F[x,v],w[u][v]),那么我么就可以用Prime算法一样的时间复杂度来求出图的次小生成树。

参考链接:http://blog.csdn.net/lyg_wangyushi/article/details/4371734

 

#include <iostream>
#include <algorithm>
#include <string.h>
#include <stdio.h>
#include <string>
#include <queue>
using namespace std;

const int INF=0x3f3f3f3f;
int n,m;
int ans1,ans2;
int w[101][101];
int dis[101];
int pre[101];
int vis[101]; 
int use[101][101]; //use[i][j]=1表示边(i,j)在最小生成树里,=0则不在
int f[101][101]; //f[u][v]表示结点u到结点v的最短路径上边的最大值(即最大边的值)
vector<int> son[101]; //son[i]存储的是与i连接的端点

void init() {
    memset(pre,0,sizeof(pre));
    memset(dis,INF,sizeof(dis));
    memset(vis,0,sizeof(vis));
    memset(f,0,sizeof(f));
    memset(use,0,sizeof(use));
}
//求最小生成树
int solve_MST() {
    init();
    vector<int> node;
    int s=1,counts=0,ans=0,tmp,k;
    dis[s]=0;
    pre[s]=s;

    node.push_back(s);  //一开始dis都赋值为INF,所以为了减少一点点遍历的时间,node存储的是dis不为INF的点
    while(1) {
        tmp=INF;
        for(int i=0; i<node.size(); i++) {
            int v=node[i];
            if(!vis[v]&& dis[v]<tmp) {
                tmp=dis[v];
                k=v;   //k即为在没有进入最小生成树的点中到树的距离(dis[k])最小的点。
            }
        }
        if(tmp==INF)
            break;
        use[k][pre[k]]=use[pre[k]][k]=1;

        for(int i=1;i<=n;i++){
            if(vis[i]){
                f[i][k]=f[k][i]=max(f[i][pre[k]],w[k][pre[k]]);
                f[i][k]=f[k][i]=max(f[pre[k]][i],w[k][pre[k]]);
            }
        }
        ans+=tmp;
        vis[k]=1;

        for(int i=0; i<son[k].size(); i++) {
            int v=son[k][i];
            if(!vis[v] && w[k][v]<dis[v]) {
                dis[v]=w[k][v];
                pre[v]=k;
                node.push_back(v);
            }
        }
    }
    return ans;

}
//求次小生成树
int second_MST(int ans){
    int second=INF;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(!use[i][j] && ans-f[i][j]+w[i][j]<second){
                second=ans-f[i][j]+w[i][j];
            }
        }
    }
    return second;
}
int main() {
    int t,a,b,c;
    scanf("%d",&t);
    for(int i=1;i<=t;i++){
        scanf("%d%d",&n,&m);
        memset(w,INF,sizeof(w));

        for(int i=0;i<101;i++){
            son[i].clear();
        }
        for(int j=1;j<=m;j++){
            scanf("%d%d%d",&a,&b,&c);
            w[a][b]=w[b][a]=c;
            son[a].push_back(b);
            son[b].push_back(a);
        }
        ans1=solve_MST();
        ans2=second_MST(ans1);
        if(ans1==ans2)
            printf("Not Unique!\n");
        else
            printf("%d\n",ans1);
    }
    return 0;
}


二:用kruskal算法

枚举删除最小生成树上的边,再求最小生成树,即总共求n-1次最小生成树,取其中最小值。

这道题如果用该方法,有一点要注意,不然的话会一直WA。

我做这道题的时候一直wrong answer的原因是因为, 可能求出的次小生成树正好等于最小生成树的总权值,但是它不连通,即边的个数小于n-1

可以试试这个测试数据:

1

6 7

1 3 1

1 2 2

2 3 3

3 4 0

4 5 4

4 6 5

5 6 6

结果是 12

就是说,如果次小生成树不连通,且最小生成树中被你删的边恰好是权值为0的情况, 这样权值总和仍相等,但不满足生成树的要求。

 

#include <iostream>
#include <algorithm>
#include <string.h>
#include <stdio.h>
#include <string>

using namespace std;

int t,n,m,cost,x,y,w; //n个节点,m条边
int ansMST,secondMST;
int numOfEdge;

struct Edge{
    int u,v;
    int cost;

    bool operator < (const Edge& tmp) const
    {
        return cost< tmp.cost;
    }

}edge[5500],MSTedge[210];

struct UF{
    int father[110];

    void unit(){
        for(int i=1;i<=n;i++){
            father[i]=i;
        }
    }

    int find_root(int x){
        if(father[x]!=x)
            father[x]=find_root(father[x]);
        return father[x];
    }

    void Union(int fa,int fb){
        father[fb]=fa;
    }
}uf;

//求最小生成树
int solveMST(){
    int counts=0;
    int ans=0;
    //int index=0;
    for(int i=0;i<m;i++){
        int u=edge[i].u;
        int v=edge[i].v;
        int fu=uf.find_root(u);
        int fv=uf.find_root(v);
        if(counts>=n-1){
            break;
        }

        if(fu!=fv){
            ans+=edge[i].cost;
            uf.Union(fu,fv);
            MSTedge[counts].u=u;
            MSTedge[counts].v=v;
            MSTedge[counts].cost=edge[i].cost;
            counts++;
        }
    }
    return ans;
}
/**
a、b表示最小生成树中删去的那条边
这里是删去(a,b)边后,求最小生成树
*/
int solve(int a,int b){
    int counts=0;
    int ans=0;
    for(int i=0;i<m;i++){
        int u=edge[i].u;
        int v=edge[i].v;
        int fu=uf.find_root(u);
        int fv=uf.find_root(v);
        if((u==a && v==b)||(u==b && v==a))
            continue;
        if(counts>=n-1){
            break;
        }
        if(fu!=fv){
            ans+=edge[i].cost;
            counts++;
            uf.Union(fu,fv);
        }
    }
    numOfEdge=counts;
    return ans;
}

int main()
{
    scanf("%d",&t);

    for(int i=1;i<=t;i++){
        scanf("%d%d",&n,&m);
        for(int j=0;j<m;j++){
            scanf("%d%d%d",&x,&y,&w);
            edge[j].u=x;
            edge[j].v=y;
            edge[j].cost=w;
        }
        sort(edge,edge+m);

        uf.unit();
        ansMST=solveMST();    //最小生成树的总权值
        secondMST=100000000;  //次小生成树的总权值
        //枚举,取最小值
        for(int j=0;j<n-1;j++){
            int u=MSTedge[j].u;
            int v=MSTedge[j].v;
            uf.unit();
            int total=solve(u,v);
            //如果它的总权值小于目前的secondMST,并且边的个数也正好为n-1
            if(total<secondMST && numOfEdge==n-1){
                secondMST=total;
            }
        }

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

 

 

posted @ 2013-08-24 20:06  辰曦~文若  阅读(268)  评论(0编辑  收藏  举报