zoukankan      html  css  js  c++  java
  • 次小生成树

    K小生成树: 生成树T删除一条边f并加入一条新边e的操作称为交换。若交换后的图仍是一颗树,则此交换称为可行交换。若生成树T可通过一次交换成为生成树T’,则称它们互为邻树。对于生成树集合S,生成树T,若T不在S中,且在S中存在某生成树是T的邻树,称为T为S的邻树。 定理:设T1, T2, …… ,  TK为图的前k小生成树,则生成图集合{T1, T2, …… , TK}的邻树中的边权和最小者可作为第k+1小生成树(可能有边权和相同的情况)。 按这个定理设计算法,很难得到有满意的时间复杂度的算法。   下面讨论一个特例:次小生成树(The second MST, 2-MST) 基本思想:首先求出最小生成树,记录权值之和为MST_NUM。然后枚举添加边(u,v),加上以后一定形成一个环,找到环上非(u,v)边的权值最大的边,把它删掉,计算当前生成树的权值之和,取所有枚举加边后生成树权值之和的最小值,就是次小生成树。 算法: 1.求出最小生成树T及其权值和MST_NUM,并标注在最小生成树上的边。 2.从每个顶点i为根,DFS遍历最小生成树,求出从i到j的路径上最大边的权值P(i, j)。 3.遍历每条不在最小生成树中的边(i,j),加上这条边,并删除环上最大边(P(i,j)),新的生成树权值之和为MST_NUM + w(i,j) - P[i][j],记录其最小值即可,时间复杂度为O(N^2)。求最小生成树可以用最简单的Prim即可,算法的瓶颈在第二步DFS遍历求路径上最大边需要O(n^2),用更好的算法是没有意义的。(PS:对于每个边,实际上可以用LCA和预处理来得到环内最大边,这样复杂度就是O(ElogV),然后配合Kruskal复杂度可以降到O(ElogV), 留待以后学习。)   练习题POJ 1679 The Unique MST  (MST是否唯一) 求出次小生成树,然后判断权值和是否等于最小生成树即可。  
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #define MID(x,y) ((x+y)>>1)
    #define mem(a,b) memset(a,b,sizeof(a))
    using namespace std;
    
    typedef long long LL;
    const int sup = 0x7fffffff;
    const int inf = -0x7fffffff;
    
    const int MAXN = 103;
    const int MAXE = 30003;
    /* 链式前向星 */
    struct node{        //gragh node
        int u, v, w;
        int op;         //无向边拆成两个有向边对应的另一个编号
        bool in_MST;    //是否在最小生成树中;
        int next;
    }edge[MAXE];
    int cnt, head[MAXN];
    void Init(){
        cnt = 0;
        mem(head, -1);
    }
    void Add_edge(int u, int v, int w){     //添加无向边
        edge[cnt].in_MST = false;
        edge[cnt].u = u;
        edge[cnt].v = v;
        edge[cnt].w = w;
        edge[cnt].next = head[u];
        edge[cnt].op = cnt + 1;
        head[u] = cnt ++;
    
        edge[cnt].in_MST = false;
        edge[cnt].u = v;
        edge[cnt].v = u;
        edge[cnt].w = w;
        edge[cnt].next = head[v];
        edge[cnt].op = cnt - 1;
        head[v] = cnt ++;
    }
    /* 链式前向星 */
    
    struct prim_node{   //prim node
        int t;      //状态节点标号
        int id;     //第几个边
        int w;      //权值
        friend bool operator < (prim_node n1, prim_node n2){
            return n1.w > n2.w;
        }
        void Init(int n){
            id = -1;
            t = n;
            w = sup;
        }
    }dist[MAXN];
    bool vis[MAXN];
    priority_queue  > Q;
    int MST_NUM;
    void Prim(int start, int n){    //初始MST_NUM=0,则对于不连通的图MST_NUM=0;
        mem(vis, 0);
        for (int i = 0; i <= n; i ++)
            dist[i].Init(i);
        while(!Q.empty())
            Q.pop();
        dist[start].w = 0;
        Q.push(dist[start]);
        while(!Q.empty()){
            prim_node tmp = Q.top();
            Q.pop();
            int u = tmp.t;
            int w = tmp.w;
            int id = tmp.id;
            if (vis[u]) continue;
            vis[u] = true;
            if (id != -1){      //确定最小生成树的边和权值和
                edge[id].in_MST = true;
                edge[edge[id].op].in_MST = true;
                MST_NUM += w;
            }
            for (int i = head[u]; i != -1; i = edge[i].next){
                int v = edge[i].v;
                int cost = edge[i].w;
                if (!vis[v] && dist[v].w > cost){
                    dist[v].w = cost;
                    dist[v].id = i;
                    Q.push(dist[v]);
                }
            }
        }
    }
    int max_cost_on_MST[MAXN][MAXN];
    bool used[MAXN];
    void dfs(int s, int t, int maxnum){
        max_cost_on_MST[s][t] = maxnum;
        used[t] = 1;
        for (int i = head[s]; i != -1; i = edge[i].next){
            if (edge[i].in_MST == false)
                continue;
            int v = edge[i].v;
            if (!used[v])   dfs(s, v, max(maxnum, edge[i].w));
        }
    }
    int n, m;
    void Second_MST(){
        Prim(1, n);
        int res = sup;
        //枚举不在最小生成树中的边
        for (int i = 1; i <= n; i ++){
            mem(used, 0);
            dfs(i, i, 0);
            for (int j = head[i]; j != -1; j = edge[j].next){
                int v = edge[j].v;
                if (edge[j].in_MST == false)
                    res = min(res, MST_NUM + edge[j].w - max_cost_on_MST[i][v]);
            }
        }
        if (res == MST_NUM){
            printf("Not Unique!\n");
        }
        else{
            printf("%d\n", MST_NUM);
        }
    }
    int main(){
        int t;
        scanf("%d", &t);
        while(t --){
            MST_NUM = 0;
            Init();
            scanf("%d %d", &n, &m);
            for (int i = 0; i < m; i ++){
                int a, b, l;
                scanf("%d %d %d", &a, &b, &l);
                Add_edge(a, b, l);
            }
            Second_MST();
        }
    	return 0;
    }
    
    举杯独醉,饮罢飞雪,茫然又一年岁。 ------AbandonZHANG
  • 相关阅读:
    urllib模块常用方法
    python Apscheduler持久化
    Java 递归(深度优先)寻找迷宫最短路径
    Java 访问修饰符
    Java 多态的一道例题
    性能测试基础(二)
    Update DataReader
    VS.Php Beta 4
    Free ASP.NET Web Development Tool
    使用 Microsoft .NET 的企业解决方案模式
  • 原文地址:https://www.cnblogs.com/AbandonZHANG/p/4114240.html
Copyright © 2011-2022 走看看