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

    最小生成树,基于并查集,把边权排序,看父节点不相同就连,最大生成树一样的。

    https://vjudge.net/contest/280903#problem/A习题集HDu1102

    下面是最简单的一种。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    
    const int maxm = 105;
    
    int pa[maxm], rak[maxm];
    int dis[maxm][maxm];
    struct node {
    int x, y, dis;
    } p[maxm * maxm];
    int comp(node n1, node n2) {
    return n1.dis < n2.dis;
    }
    void make_set(int x) {
    pa[x] = x;
    rak[x] = 0;
    }
    int findx(int x)
    {
        int r = x, temp;
        while(pa[r] != r) r = pa[r];
        while(x != r)
        {
            temp = pa[x];
            pa[x] = r;
            x = temp;
        }
        return x;
    
    }
    
    void unio(int x, int y)
    {
        x = findx(x);
        y = findx(y);
        if(x == y)return ;
        if(rak[x] > rak[y])
        {
            pa[y] = x;
        }
        else
        {
            pa[x] = y;
            if(rak[x] == rak[y])
                rak[y]++;
        }
    }
    int n, m, x, y, nn;
    int main() {
    while(~scanf("%d", &n)){
    int ant = 0;
    for(int i = 1; i <= n; i++) {
        make_set(i);
    }
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) {
            p[ant].x = i;
            p[ant].y = j;
            scanf("%d", &nn);
            p[ant].dis = nn;
            ant++;
        }
    }
    scanf("%d", &m);
    while(m--) {
        scanf("%d%d", &x, &y);
        unio(x, y);
    }
    sort(p, p + n * n, comp);
    int res = 0;
    for(int i = 0; i < n * n; i++) {
        if(findx(p[i].x) != findx(p[i].y)) {
            res += p[i].dis;
            unio(p[i].x, p[i].y);
        }
    }
    printf("%d
    " ,res);
    }
    return 0;
    }

    Slim Span

     UVA - 1395

    find the most comfortable road

     HDU - 1598 

    这两个题目都是求一条最小生成树,然后对权值差有所要求。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    
    const int maxm = 205;
    const int maxm1 = 1005;
    typedef pair<double, double> pii;
    typedef long long ll;
    int pa[maxm], rak[maxm];
    int num[maxm];
    struct node {
    int x, y;
    int  dis;
    } p[maxm1];
    pii point[maxm];
    int comp(node n1, node n2) {
    return n1.dis < n2.dis;
    }
    void make_set(int x) {
    pa[x] = x;
    rak[x] = 0;
    }
    int findx(int x)
    {
        int r = x, temp;
        while(pa[r] != r) r = pa[r];
        while(x != r)
        {
            temp = pa[x];
            pa[x] = r;
            x = temp;
        }
        return x;
    
    }
    
    void unio(int x, int y)
    {
        x = findx(x);
        y = findx(y);
        if(x == y)return ;
        if(rak[x] > rak[y])
        {
            pa[y] = x;
        }
        else
        {
            pa[x] = y;
            if(rak[x] == rak[y])
                rak[y]++;
        }
    }
    int n, m, k;
    double d;
    char ch[3];
    int main() {
    while(~scanf("%d%d", &n, &m)) {
        memset(num, 0, sizeof(num));
        for(int i = 0; i < m; i++) {
            scanf("%d%d%d", &p[i].x, &p[i].y, &p[i].dis);
    //        num[ p[i].x ]++;
    //        num[ p[i].y ]++;
        }
        sort(p, p + m, comp);
        scanf("%d", &k);
        int l, r;
        while(k--) {
            scanf("%d%d", &l, &r);
    
            int minn = 1e9 + 7;
            int flag = 0;
            for(int i = 0; i < m; i++) {
                for(int i = 1; i <= n; i++) {
                    make_set(i);
                }
                for(int j = i; j < m; j++) {
                    unio(p[j].x, p[j].y);
                    if(findx(l) == findx(r)) {
                        minn = min(minn, p[j].dis - p[i].dis);
                        flag = 1;
                        break;
                    }
                }
            }
    //这题是看L到R是否联通,所以先排序,然后枚举最小距离,在循环找到最大距离,在相减。
    if(flag) printf("%d ", minn); else printf("-1 "); } } return 0; }

     https://vjudge.net/contest/280903#problem/EHDu3367

    Pseudoforest

    伪森林的定义
    1,一个无向图;
    2,它的所有连通分量最多只有一个环;
    3. 伪森林的大小取决于图中所有边的边权之和。

    题意:给你一个无向图,让你求出图中最大的伪森林。

    这道题用到最大生成树的实现方法(kruskal)。关键在于并查集部分的处理

    一:两点不在一颗树上
    1,两点都不在环里,直接增边;
    2,两点只有一个在环里,增边后并标记两点都在环里面;
    3,两点都在环里面,增边后生成树一定会有至少两个环,不符;

    二:两点在一棵树上
    1,两点都不在环里,直接增边,并标记;
    2,两点只有一个在环里,增边后生成树会有至少两个环,不符;
    3,两点都在环里面,增边后生成树一定会有至少两个环,不符;

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    
    const int maxm = 1e4 + 5;
    const int maxm1 = 1e5 + 6;
    typedef pair<double, double> pii;
    typedef long long ll;
    int pa[maxm], rak[maxm];
    int vis[maxm];
    struct node {
    int x, y;
    int  dis;
    } p[maxm1];
    pii point[maxm];
    int comp(node n1, node n2) {
    return n1.dis > n2.dis;
    }
    void make_set(int x) {
    pa[x] = x;
    rak[x] = 0;
    }
    int findx(int x)
    {
        int r = x, temp;
        while(pa[r] != r) r = pa[r];
        while(x != r)
        {
            temp = pa[x];
            pa[x] = r;
            x = temp;
        }
        return x;
    
    }
    
    void unio(int x, int y)
    {
        x = findx(x);
        y = findx(y);
        if(x == y)return ;
        if(rak[x] > rak[y])
        {
            pa[y] = x;
        }
        else
        {
            pa[x] = y;
            if(rak[x] == rak[y])
                rak[y]++;
        }
    }
    int n, m;
    double d;
    char ch[3];
    int main() {
    while(~scanf("%d%d", &n, &m)) {
        if(n == 0 && m == 0) break;
        memset(vis, 0, sizeof(vis));
        for(int i = 0; i < n; i++) {
            make_set(i);
        }
        for(int i = 0; i < m; i++) {
            scanf("%d%d%d", &p[i].x, &p[i].y, &p[i].dis);
        }
        sort(p, p + m, comp);
        ll res = 0;
        for(int i = 0; i < m; i++) {
            if(findx(p[i].x) != findx(p[i].y)) {
                if(vis[ findx(p[i].x) ] && vis[ findx(p[i].y) ]) continue;
                else if((vis[ findx(p[i].x) ] && !vis[ findx(p[i].y)] ) || (!vis[ findx(p[i].x) ] && vis[ findx(p[i].y)] )) {
                    vis[ findx(p[i].x) ] = 1;
                    vis[ findx(p[i].y) ] = 1;
                    res += p[i].dis;
                    unio(p[i].x, p[i].y);
                }
                else {
                    res += p[i].dis;
                    unio(p[i].x, p[i].y);
                }
            }
            else {
                if(!vis[ findx(p[i].x) ]) {
                    res += p[i].dis;
                    unio(p[i].x, p[i].y);
                    vis[ findx(p[i].x) ] = 1;
                }
            }
        }
        printf("%lld
    ", res);
    }
    return 0;
    }

    Buy or Build

     UVA - 1151

    难题,有现成的已经连在一起的,有固定的价格,其他两个连在一起的价格是他们距离的平方,然后求最小生成树的消费。

    其中有个二进制枚举#include<cstdio>

    #include<algorithm>
    #include<cstring>
    using namespace std;
    using namespace std;
    const int maxm = 1005;
    int t, n , m, rec, q, cnt, pa[maxm], x[maxm],y[maxm];
    struct node {
        int n , c, a[maxm];//结构体里面存数组
    }p[9];
    struct edge{
        int a, b, v;
    }ed[maxm * maxm], e[maxm];
    bool comp(edge a, edge b) {
        return a.v < b.v;
    }
    int findx(int x) {
    return pa[x] == x ? x : pa[x] = findx(pa[x]);
    }
    int solve() {
        sort(ed, ed + cnt, comp);
        int ans = 0;
        rec = 0;
      //先拿到初始的最小生成树的大小。
    for(int i = 0; i < cnt; i++) { int x = findx(ed[i].a) , y = findx(ed[i].b); if(x != y) { ans += ed[i].v; e[rec].a = ed[i].a; e[rec].b = ed[i].b; e[rec].v = ed[i].v; rec++; pa[x] = y; } }
      //二进制枚举。
      //第一个for循环是为了得到所有的情况,用二进制表示,第二个是为了把1提取出来。
    for(int s = 0; s < (1 << q); s++) { for(int j = 1; j <= n; j++) pa[j] = j; int cur = 0; for(int j = 0; j < q; j++) {
    if(s & (1 << j)) { cur += p[j].c; for(int i = 1; i <= p[j].n; i++) { int x = findx(p[j].a[i]) ,y = findx(p[j].a[1]); if(x != y) pa[x] = y; } } }
      //补边
    for(int i = 0; i < rec; i++) { int x = findx(e[i].a) , y = findx(e[i].b); if(x != y) { cur += e[i].v; pa[x] = y; } } ans = min(ans,cur); } return ans; } int main() { scanf("%d", &t); while(t--) { scanf("%d%d", &n, &q); for(int i = 0; i < q; i++) { scanf("%d%d", &p[i].n, &p[i].c); for(int j = 1; j <= p[i].n; j++) scanf("%d", &p[i].a[j]); } for(int i = 1; i <= n; i++) scanf("%d%d", &x[i], &y[i]); cnt = 0; for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { int v = (x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j]) * (y[i] - y[j]); ed[cnt].a = i; ed[cnt].b = j; ed[cnt].v = v; cnt++; } } for(int i = 1; i <= n; i++) pa[i] = i; printf("%d ",solve()); if(t) printf(" "); } return 0; }

    https://blog.csdn.net/yuanhanchun/article/details/76735225 prime算法,更克鲁斯卡尔差不多

    https://www.cnblogs.com/H-Vking/p/4317009.html

  • 相关阅读:
    DispatcherServlet
    上转型对象
    Javascript闭包(Closure)
    跨域
    dict
    Python 函数参数传递方式
    协同过滤
    白话 动态规划 第一节 初识动态规划
    Spring@Autowired注解与自动装配
    protected
  • 原文地址:https://www.cnblogs.com/downrainsun/p/10331050.html
Copyright © 2011-2022 走看看