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

    本来想好好写的....连续电脑挂了3次....就转载一篇了....

    先看一个结论:次小生成树可由最小生成树换一条边得到

             证明:咱换种方式去看待这个结论(一个生成树可以通过换边得到另一个生成树),T是某一棵最小生成树,T0是任一棵异于T的生成树,通过变换T0 --> T1 --> T2 --> ... --> Tn (T)  变成最小生成树。所谓的变换是,每次把Ti中的某条边换成T中的一条边, 而且树T(i+1)的权小于等于Ti的权。

             看下面的具体步骤(一定要理解透彻)。 
             step 1. 在Ti中任取一条不在T中的边uv. 
             step 2. 把边uv去掉,就剩下两个连通分量A和B,在T中,必有唯一的边u'v' 连结A和B。这是为什么呢?因为生成树中任意两点间只有一条路径(下面也要用这个),且必有一条。 
             step 3. 显然u'v'的权比uv小 (prime算法贪心的,否则,uv就应该在T中),把u'v'替换uv即得树T(i+1)。 
             特别地:取T0为任一棵次小生成树,T(n-1) 也就是次小生成树且跟T差一条边, 结论得证。

    方法是:

    1、找到最小生成树,值为mst

    2、最小生成树种的点:找到每一个点到其它点的路径上的最大边权值 dp[i][j]表示i到j路径上的最大边权值

    3、加一条不在最小生成树上的边。比如i - k,同时删除在最小生成树上i -> k路径上最大的一个边权值dp[i][k]; 这样会得到 new_mst,在这些new_mst中找一个最小的,就是次小生成树的值


    实现上用到一些技巧,代码如下POJ 1679

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    #include <string>
    #include <set>
    #include <ctime>
    #include <queue>
    #include <map>
    #include <sstream>


    #define CL(arr, val)    memset(arr, val, sizeof(arr))
    #define REP(i, n)       for((i) = 0; (i) < (n); ++(i))
    #define FOR(i, l, h)    for((i) = (l); (i) <= (h); ++(i))
    #define FORD(i, h, l)   for((i) = (h); (i) >= (l); --(i))
    #define L(x)    (x) << 1
    #define R(x)    (x) << 1 | 1
    #define MID(l, r)   (l + r) >> 1
    #define Min(x, y)   x < y ? x : y
    #define Max(x, y)   x < y ? y : x
    #define E(x)    (1 << (x))


    const int eps = 1e-6;
    const int inf = ~0u>>2;
    typedef long long LL;


    using namespace std;


    const int N = 110;
    const int M = 100000;


    struct node {
        int to;
        int next;
    } G[M];


    int mp[N][N];
    int dis[N];
    int pre[N];
    bool vis[N];


    int head[N], t;
    int n;


    bool inMST[N][N];
    int dp[N][N];


    void add(int u, int v) {
        G[t].to = v; G[t].next = head[u]; head[u] = t++;
    }


    int prim() {
        int i, j, f, mx, ret = 0;
        for(i = 1; i <= n; ++i)   {
            dis[i] = inf;
            pre[i] = 0;
            vis[i] = false;
        }
        pre[1] = 0;
        dis[1] = 0;
        for(i = 1; i <= n; ++i) {
            mx = inf; f = 0;
            for(j = 1; j <= n; ++j) {
                if(!vis[j] && mx > dis[j]) {
                    mx = dis[j];
                    f = j;
                }
            }
            vis[f] = true;
            ret += mx;
            for(j = 1; j <= n; ++j) {
                if(!vis[j] && dis[j] > mp[f][j]) {
                    pre[j] = f;
                    dis[j] = mp[f][j];
                }
            }
        }
        for(i = 1; i <= n; ++i) {   //记录在最小生成树上的边
            if(pre[i]) inMST[pre[i]][i] = inMST[i][pre[i]] = true;
        }


        for(i = 1; i <= n; ++i) {    //建新图,用来找在最小生成树上 i->j路径中最大的一个边权值
            if(pre[i])  {add(pre[i], i); add(i, pre[i]); }
            //printf("%d %d ", i , pre[i]);
        }
        return ret;
    }


    void init() {
        int i, j;
        for(i = 1; i <= n; ++i) {
            for(j = 1; j <= n; ++j) {
                mp[i][j] = (i == j ? 0 : inf);
            }
        }
        CL(head, -1); t = 0;
        CL(inMST, false);
        CL(dp, 0);
    }


    void dfs(int st, int to, int val) {    //。。。
        vis[to] = true;
        dp[st][to] = val;
        for(int i = head[to]; i != -1; i = G[i].next) {
            if(!vis[G[i].to])
                dfs(st, G[i].to, max(val, mp[to][G[i].to]));
        }
    }


    int main() {
        //freopen("data.in", "r", stdin);


        int t, m, i, j;
        int u, v, w;
        int ans, mst;
        scanf("%d", &t);
        while(t--) {
            scanf("%d%d", &n, &m);
            init();
            while(m--) {
                scanf("%d%d%d", &u, &v, &w);
                mp[u][v] = mp[v][u] = w;
            }


            mst = prim(); ans = inf;
            for(i = 1; i <= n; ++i) {
                CL(vis, false);
                dfs(i, i, 0);
            }


            for(i = 1; i <= n; ++i) {
                for(j = i + 1; j <= n; ++j) {
                    if(!inMST[i][j] && mp[i][j] != inf)    //加边
                        ans = min(ans, mst - dp[i][j] + mp[i][j]);
                }
            }
            if(ans == mst)  puts("Not Unique!");
            else    printf("%d ", mst);
        }
        return 0;
    }

  • 相关阅读:
    ckeditor添加自定义按钮整合swfupload实现批量上传图片
    H5移动端适配之px转vw(附工具)
    原生js实现复制文本到粘贴板
    快速删除项目中的输出日志console.log
    toString和valueOf使得对象访问时显示一个特定格式的字符串,但是可以进行数字运算
    __defineGetter__和__defineSetter__在日期中的应用
    观察者模式(订阅-发布者模式)
    原生js扫雷代码
    身份证验证思路及代码
    IMEI校验思路及代码
  • 原文地址:https://www.cnblogs.com/luckycode/p/5255660.html
Copyright © 2011-2022 走看看