zoukankan      html  css  js  c++  java
  • 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!
    ");
            else
                printf("%d
    ",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!
    ");
            else
                printf("%d
    ",ansMST);
        }
        return 0;
    }
  • 相关阅读:
    分页的实现
    调取地图map
    meta标签应用,适应手机屏幕以及关键词、描述的添加
    页面中公共部分的统一调用
    PC端变成手机端的时候,把特效去掉(把canvas标签去掉)
    IIS上绑定域名,发布上线
    动态截取字符串获取当前网页的URL地址
    Vue 打包后报错 Uncaught TypeError: Cannot redefine property: $router
    Vue项目部署到线上页面空白
    让从后台返回的数据在让elementui 的el-select 显示对应的label值而不是value值
  • 原文地址:https://www.cnblogs.com/chenxiwenruo/p/3279771.html
Copyright © 2011-2022 走看看