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

    黑书+论文+各种资料。终于理解了一点。。。

    最小度限制生成树就是给一个图,让求它的最小生成树。找的的最小生成树满足并且点vo的度最大为k。

    算法流程如下:
    1.将该点(以下用v0表示)从图中删除,将得到m个连通分量。
    2.对每个连通分量求最小生成树,假设m个。
    3.从每个连通分量中找与v0关联的权值最小的边,与v0相连接,这样将得到v0的最小m度生成树
    4.如果 k  < m 那么这种树是不存在的。
    5.如果 k >=m ,那么考虑构建 m+1度 最小生成树 ,将与v0关联的且不在当前的树中的边
    6.如果将其加入树中 ,必然会存在一个环,那么删掉该环中与v0不关联的权值最大边,将得到加入该边后的最小生成树,且是m+1的。
    7.枚举上述 6 的边找树权值最小,那么即是m+1度限制的最小生成树。如果 m + 1 度最小生成树的值大于 m 度最小生成树的话直接输出当前 m  度的值即可。
    8.重复5.6.7,直到k 度最小生成树出现。

    由于从度为m扩展到m+1时存在大量的重复计算,可以用动态规划优化。

    以下引用自汪汀的论文

    由最小m度限制生成树,得到最小m+1度限制生成树,对于和V0相邻的点v,则可以知道一定会有一个环出现,只要找到这个环上的最大权边,用边(V0, v)替换掉,就可以得到一个m+1度限制生成树,枚举所有和V0相邻点v,找到替换后增加权值最小的一次替换,就可以求得m+1度限制生成树。。如果每添加一条边,都需要对环上的边一一枚
    举,时间复杂度将比较高,这里,动态规划就有了用武之地。设Best(v)为路径v0—v上与v0无关联且权值最大的边。定义father(v)为v的父结点,动态转移方程:Best(v)=max(Best(father(v)),ω(father(v),v)),边界条件为Best[v0]=-∞,Best[v’]=-∞| (v0,v’)∈E(T)。

    模板 POJ 1639

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    #include <vector>
    #include <cstring>
    #include <algorithm>
    #include <string>
    #include <set>
    #include <ctime>
    #include <queue>
    #include <map>
    
    #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 double eps = 1e-8;
    typedef long long LL;
    using namespace std;
    const int inf = ~0u>>2;
    const int N = 33;
    
    int parent[N];
    int g[N][N];
    bool flag[N][N];
    map<string, int> NUM;
    
    int n, k, cnt, ans;
    
    struct node {
        int x;
        int y;
        int v;
    } a[1<<10];
    
    struct edge {
        int x;
        int y;
        int v;
    } dp[N];
    
    bool cmp(node a, node b) {
        return a.v < b.v;
    }
    
    int find(int x) {   //并查集查找
        int k, j, r;
        r = x;
        while(r != parent[r]) r = parent[r];
        k = x;
        while(k != r) {
            j = parent[k];
            parent[k] = r;
            k = j;
        }
        return r;
    }
    
    int get_num(string s) {    //求编号
        if(NUM.find(s) == NUM.end()) {
            NUM[s] = ++cnt;
        }
        return NUM[s];
    }
    
    void kruskal() {  //。。。
        int i;
        FOR(i, 1, n) {
            if(a[i].x == 1 || a[i].y == 1)  continue;
            int x = find(a[i].x);
            int y = find(a[i].y);
            if(x == y)  continue;
            flag[a[i].x][a[i].y] = flag[a[i].y][a[i].x] = true;
            parent[y] = x;
            ans += a[i].v;
        }
       //printf("%d\n", ans);
    }
    
    void dfs(int x, int pre) {   //dfs求1到某节点路程上的最大值
        int i;
        FOR(i, 2, cnt) {
            if(i != pre && flag[x][i]) {
                if(dp[i].v == -1) {
                    if(dp[x].v > g[x][i])   dp[i] = dp[x];
                    else {
                        dp[i].v = g[x][i];
                        dp[i].x = x;    //记录这条边
                        dp[i].y = i;
                    }
                }
                dfs(i, x);
            }
        }
    }
    
    void init() {
        ans = 0; cnt = 1;
        CL(flag, false);
        CL(g, -1);
        NUM["Park"] = 1;
        for(int i = 0; i < N; ++i)  parent[i] = i;
    }
    
    int main() {
        //freopen("data.in", "r", stdin);
    
        int i, j, v;
        string s;
        scanf("%d", &n);
        init();
        for(i = 1; i <= n; ++i) {
            cin >> s;
            a[i].x = get_num(s);
            cin >> s;
            a[i].y = get_num(s);
            scanf("%d", &v);
            a[i].v = v;
            if(g[a[i].x][a[i].y] == -1)     g[a[i].x][a[i].y] = g[a[i].y][a[i].x] = v;
            else    g[a[i].x][a[i].y] = g[a[i].y][a[i].x] = min(g[a[i].x][a[i].y], v);
        }
        scanf("%d", &k);
        int set[N], Min[N];
        REP(i, N)   Min[i] = inf;
        sort(a + 1, a + n + 1, cmp);
        kruskal();
        FOR(i, 2, cnt) {    //找到1到其他连通块的最小值
            if(g[1][i] != -1) {
                int x = find(i);
                if(Min[x] > g[1][i]) {
                    Min[x] = g[1][i];
                    set[x] = i;
                }
            }
        }
        int m = 0;
        FOR(i, 1, cnt) {  //把1跟这些连通块连接起来
            if(Min[i] != inf) {
                m++;
                flag[1][set[i]] = flag[set[i]][1] = true;
                ans += g[1][set[i]];
            }
        }
        //printf("%d\n", ans);
        for(i = m + 1; i <= k; ++i) {  //从度为m+1一直枚举到最大为k,找ans的最小值
            CL(dp, -1);
            dp[1].v = -inf;   //dp初始化
            for(j = 2; j <= cnt; ++j) {
                if(flag[1][j])  dp[j].v = -inf;
            }
            dfs(1, -1);
            int tmp, mi = inf;
            for(j = 2; j <= cnt; ++j) {    
                if(g[1][j] != -1) {
                    if(mi > g[1][j] - dp[j].v) {    //找到一条dp到连通块中某个点的边,替换原来连通块中的边(前提是新找的这条边比原来连通块中那条边要大)
                        mi = g[1][j] - dp[j].v;
                        tmp = j;
                    }
                }
            }
            if(mi >= 0) break;    //如果不存在这样的边,直接退出
            int x = dp[tmp].x, y = dp[tmp].y;
    
            flag[1][tmp] = flag[tmp][1] = true;   //加上新找的边
            flag[x][y] = flag[y][x] = false;    //删掉被替换掉的那条边
    
            ans += mi;
        }
        printf("Total miles driven: %d\n", ans);
    
        return 0;
    
    }
  • 相关阅读:
    (原)学以致用:用数学公式'幂函数'支持生产经营分析
    CString 成员函数用法大全
    致hr新人的一封信
    [恢]hdu 2560
    [恢]hdu 1907
    [恢]hdu 1267
    [恢]hdu 2554
    [恢]hdu 1329
    [恢]hdu 2317
    [恢]hdu 2555
  • 原文地址:https://www.cnblogs.com/vongang/p/2575383.html
Copyright © 2011-2022 走看看