最小生成树

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

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\n" ,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\n", minn); else printf("-1\n"); } } 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\n", 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\n",solve()); if(t) printf("\n"); } return 0; }

 

 

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

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

posted @ 2019-01-28 17:22  downrainsun  阅读(143)  评论(0编辑  收藏  举报