zoukankan      html  css  js  c++  java
  • 《挑战程序设计竞赛》2.5 最小生成树 POJ3723 3169 1258 2377 2395 AOJ2224(1)

    POJ3723

    http://poj.org/problem?id=3723

    题意

    windy要组建一支军队,召集了N个女孩和M个男孩,每个人要付10000RMB,但是如果一个女孩和一个男孩有关系d的,且已经付给了其中一个人的钱,那么就可以付给另一个人10000-d元,求windy最少要付多少钱。

    思路

    题目所给的数据是两两之间的连通关系,比较适合用kruskal+并查集求解。
    但这个题要求的是最大生成树,不是最小生成树哦,需要修改比较条件。当然将d取反再求最小生成树也是一样的。

    代码

    Source Code
    
    Problem: 3723       User: liangrx06
    Memory: 996K        Time: 360MS
    Language: C++       Result: Accepted
    Source Code
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    const int N = 20000;
    const int R = 50000;
    
    struct Node {
        int x, y, d;
    };
    
    int n, r;
    Node node[R+1];
    int pre[N+1];
    int rank[N+1];
    
    bool cmp(const Node& a, const Node& b)
    {
        return a.d < b.d;
    }
    
    void init()
    {
        for (int i = 1; i <= n; i ++) {
            pre[i] = i;
            rank[i] = 0;
        }
    }
    
    int find(int a)
    {
        while (a != pre[a])
            a = pre[a];
        return a;
    }
    
    void unite(int a, int b)
    {
        a = find(a);
        b = find(b);
        if (a == b) return;
        if (rank[a] < rank[b]) {
            pre[a] = b;
        } else {
            pre[b] = a;
            if (rank[a] == rank[b])
                rank[a] ++;
        }
    }
    
    bool same(int a, int b)
    {
        return find(a) == find(b);
    }
    
    int kruskal()
    {
        sort(node+1, node+r+1, cmp);
        int res = 0;
        for (int i = 1; i <= r; i ++) {
            int x = node[i].x, y = node[i].y;
            if (!same(x, y)) {
                unite(x, y);
                res += node[i].d;
            }
        }
        return res;
    }
    
    int main(void)
    {
        int t, n1, n2;
        cin >> t;
        while (t--) {
            cin >> n1 >> n2 >> r;
            n = n1 + n2;
            int x, y, d;
            for (int i = 1; i <= r; i ++) {
                scanf("%d%d%d", &x, &y, &d);
                node[i].x = x + 1;
                node[i].y = n1 + y + 1;
                node[i].d = -d;
            }
            init();
            printf("%d
    ", 10000 * n + kruskal());
        } 
    
        return 0;
    }   

    POJ3169

    POJ1258

    http://poj.org/problem?id=1258

    题意

    有n个农场,已知这n个农场都互相相通,有一定的距离,现在每个农场需要装光纤,问怎么安装光纤能将所有农场都连通起来,并且要使光纤距离最小,输出安装光纤的总距离。

    思路

    求最小生成树,由于这个题直接给出了邻接矩阵,所以用prim算法比较合适。

    代码

    Source Code
    
    Problem: 1258       User: liangrx06
    Memory: 212K        Time: 16MS
    Language: C++       Result: Accepted
    Source Code
    #include <iostream>
    #include <cstdio>
    using namespace std;
    
    const int N = 100;
    const int INF = 0x3f3f3f3f;
    
    int n;
    int d[N][N];
    int md[N];
    bool used[N];
    
    int prim()
    {
        fill(md, md+n, INF);
        fill(used, used+n, false);
        md[0] = 0;
        int res = 0;
        while (true) {
            int v = -1;
            for (int i = 0; i < n; i ++) {
                if (!used[i] && (v == -1 || md[i] < md[v]))
                    v = i;
            }
            if (v == -1) break;
            used[v] = true;
            res += md[v];
            for (int i = 0; i < n; i ++) {
                md[i] = min(md[i], d[v][i]);
            }
        }
        return res;
    }
    
    int main(void)
    {
        while (scanf("%d", &n) != EOF) {
            for (int i = 0; i < n; i ++) {
                for (int j = 0; j < n; j ++) {
                    scanf("%d", &d[i][j]);
                }
            }
            printf("%d
    ", prim());
        } 
    
        return 0; 
    } 

    POJ2377

    http://poj.org/problem?id=2377

    题意

    给定一些两点之间的连通距离,求最大生成树。

    思路

    题目所给的数据是两两之间的连通关系,比较适合用kruskal+并查集求解。
    但这个题要求的是最大生成树,排序的时候按降序排列或者将距离取反最后结果再取反就可以了。

    代码

    Source Code
    
    Problem: 2377       User: liangrx06
    Memory: 496K        Time: 32MS
    Language: C++       Result: Accepted
    Source Code
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    const int N = 1000;
    const int M = 20000;
    
    struct Node {
        int x, y, d;
    };
    
    int n, m;
    Node node[M+1];
    int pre[N+1];
    int rank[N+1];
    
    bool cmp(const Node& a, const Node& b)
    {
        return a.d < b.d;
    }
    
    void init()
    {
        for (int i = 1; i <= n; i ++) {
            pre[i] = i;
            rank[i] = 0;
        }
    }
    
    int find(int a)
    {
        while (a != pre[a])
            a = pre[a];
        return a;
    }
    
    void unite(int a, int b)
    {
        a = find(a);
        b = find(b);
        if (a == b) return;
        if (rank[a] < rank[b]) {
            pre[a] = b;
        } else {
            pre[b] = a;
            if (rank[a] == rank[b])
                rank[a] ++;
        }
    }
    
    bool same(int a, int b)
    {
        return find(a) == find(b);
    }
    
    int kruskal()
    {
        sort(node+1, node+m+1, cmp);
        int res = 0;
        int uniteTime = 0;
        for (int i = 1; i <= m; i ++) {
            int x = node[i].x, y = node[i].y;
            if (!same(x, y)) {
                unite(x, y);
                uniteTime ++;
                res += node[i].d;
            }
        }
        if (uniteTime == n-1)
            return -res;
        else
            return -1;
    }
    
    int main(void)
    {
        cin >> n >> m;
        for (int i = 1; i <= m; i ++) {
            scanf("%d%d%d", &node[i].x, &node[i].y, &node[i].d);
            node[i].d *= -1;
        }
        init();
        printf("%d
    ", kruskal());
    
        return 0;
    }

    POJ2395

    http://poj.org/problem?id=2395

    题意

    有N个农场,它们是连通的,现在你要从1号农场找到路走到其他所有农场去。但是有个要求就是你必须使得你将要走的单段路的最大长度最小。也就是说,任意两个农场之间的路如果被你选中要走的话,那么这种单段路的最大值必须尽量小。
    

    思路

    其实就是要你选一些路使得所有农场属于同一个连通分量,且要求你输出最大边的值. 仔细想想kruskal算法,它从所有边长从小到大的顺序开始选边,每次选边都使得两个点连通. 可以证明kruskal算法所选择的最后一条边即是我们本题的答案.
    
    最小瓶颈生成树在刘汝佳<<训练指南>>P343页有详细的介绍.
    
    无向图G的一颗瓶颈生成树(bottleneck spanning tree)T是这样的一颗生成树,它最大的边权值在G的所有生成树中是最小的。
    
    无向图的最小生成树一定是瓶颈生成树,但瓶颈生成树不一定是最小生成树。(来自百度百科:瓶颈生成树)
    

    代码

    Source Code
    
    Problem: 2395       User: liangrx06
    Memory: 388K        Time: 79MS
    Language: C++       Result: Accepted
    Source Code
    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    const int N = 2000;
    const int M = 10000;
    
    struct Node {
        int x, y, d;
    };
    
    int n, m;
    Node node[M+1];
    int pre[N+1];
    int rank[N+1];
    
    bool cmp(const Node& a, const Node& b)
    {
        return a.d < b.d;
    }
    
    void init()
    {
        for (int i = 1; i <= n; i ++) {
            pre[i] = i;
            rank[i] = 0;
        }
    }
    
    int find(int a)
    {
        while (a != pre[a])
            a = pre[a];
        return a;
    }
    
    void unite(int a, int b)
    {
        a = find(a);
        b = find(b);
        if (a == b) return;
        if (rank[a] < rank[b]) {
            pre[a] = b;
        } else {
            pre[b] = a;
            if (rank[a] == rank[b])
                rank[a] ++;
        }
    }
    
    bool same(int a, int b)
    {
        return find(a) == find(b);
    }
    
    int kruskal()
    {
        sort(node+1, node+m+1, cmp);
        int res = 0;
        int uniteTime = 0;
        for (int i = 1; i <= m; i ++) {
            int x = node[i].x, y = node[i].y;
            if (!same(x, y)) {
                unite(x, y);
                uniteTime ++;
                res = node[i].d;
            }
        }
        if (uniteTime == n-1)
            return res;
        else
            return -1;
    }
    
    int main(void)
    {
        cin >> n >> m;
        for (int i = 1; i <= m; i ++) {
            scanf("%d%d%d", &node[i].x, &node[i].y, &node[i].d);
        }
        init();
        printf("%d
    ", kruskal());
    
        return 0;
    }

    AOJ2224

    http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=2224

    题意

    有N个木桩M个栅栏,栅栏连接木桩,现在这些栅栏围成的封闭空间里有至少一只猫,要求破环若干个栅栏救出猫,问破环栅栏的最小长度。
    

    思路

    题目要求栅栏不组成圈,又要求破坏栅栏的最小长度,那么剩下的栅栏自然是最大生成树了(当然前提是图是连通的)。kruskal+并查集求最大生成树,在从大到小添加边的过程中,如果发现新添加的边的两个节点已经连通,说明这个边需要去掉,累加这个边的长度到结果变量res。
    另外,即使这个图非连通,这样的破圈求解过程仍然能够得到正确答案。
    

    代码

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    
    const int N = 10000;
    const int M = 10000*10000/2;
    
    struct Node {
        int x, y;
        double d;
    };
    
    int n, m;
    Node node[M+1];
    int pre[N+1];
    int rank[N+1];
    
    bool cmp(const Node& a, const Node& b)
    {
        return a.d > b.d;
    }
    
    void init()
    {
        for (int i = 1; i <= n; i ++) {
            pre[i] = i;
            rank[i] = 0;
        }
    }
    
    int find(int a)
    {
        while (a != pre[a])
            a = pre[a];
        return a;
    }
    void unite(int a, int b)
    {
        a = find(a);
        b = find(b);
        if (a == b) return;
        if (rank[a] < rank[b]) {
            pre[a] = b;
        } else {
            pre[b] = a;
            if (rank[a] == rank[b])
                rank[a] ++;
        }
    }
    
    bool same(int a, int b)
    {
        return find(a) == find(b);
    }
    
    double distance(int x1, int y1, int x2, int y2)
    {
        return sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
    }
    
    double kruskal()
    {
        sort(node+1, node+m+1, cmp);
        double res = 0;
        for (int i = 1; i <= m; i ++) {
            int x = node[i].x, y = node[i].y;
            if (!same(x, y))
                unite(x, y);
            else
                res += node[i].d;
        }
        return res;
    }
    
    int main(void)
    {
        int a[N+1], b[N+1];
        cin >> n >> m;
        for (int i = 1; i <= n; i ++) {
            scanf("%d%d", &a[i], &b[i]);
        }
        for (int i = 1; i <= m; i ++) {
            scanf("%d%d", &node[i].x, &node[i].y);
            int x = node[i].x, y = node[i].y;
            node[i].d = distance(a[x], b[x], a[y], b[y]);
        }
        init();
        printf("%.3lf
    ", kruskal());
    
        return 0;
    }
  • 相关阅读:
    2017-5-15 winform项目总结(知识点补充)
    2017-5-7 time控件 三级联动(省,市,区)
    2017-5-4 进程 线程 用户控件
    2017-5-3 打印控件 MDI 窗体容器 Activated事件
    2017-5-2 对话框控件 MessageBox.Show()用法补充 打开新窗体的3中模式
    窗体移动 窗体阴影API
    2017-4-28 ListView控件学习
    【2017-03-28】JS基础、DOM操作
    【2017-03-24】样式表样式
    【2017-03-24】CSS样式表
  • 原文地址:https://www.cnblogs.com/liangrx06/p/5083763.html
Copyright © 2011-2022 走看看