洛谷—题解 P1111 修复公路

P1111 修复公路原题链接
题目背景
AA地区在地震过后,连接所有村庄的公路都造成了损坏而无法通车。政府派人修复这些公路。

题目描述
给出A地区的村庄数NN,和公路数MM,公路是双向的。并告诉你每条公路的连着哪两个村庄,并告诉你什么时候能修完这条公路。问最早什么时候任意两个村庄能够通车,即最早什么时候任意两条村庄都存在至少一条修复完成的道路(可以由多条公路连成一条道路)

输入输出格式
输入格式:
第11行两个正整数N,MN,M
下面MM行,每行33个正整数x, y, tx,y,t,告诉你这条公路连着x,yx,y两个村庄,在时间t时能修复完成这条公路。

输出格式:
如果全部公路修复完毕仍然存在两个村庄无法通车,则输出-1−1,否则输出最早什么时候任意两个村庄能够通车。

输入输出样例

输入样例#1:

4 4
1 2 6
1 3 4
1 4 5
4 2 3

输出样例#1:

5

题解:
先简单的看一下题目,题目是关于图的最小生成树的问题。
既然要求 ,我们自然而然的可以想到首先选择最短的边,然后选择次短的边…直到选择了n-1条边。这就需要先对所有的边按照权值进行从小到大排序,然后从最小的开始选,依次选择每一条边,加入到生成树中,直到选择了n-1条边让整个图连通为止。

注意:在选边加入树的时候,不要形成回路,那就不是树了,依次需要跳过这类边

刚才的操作中,比较难实现的是:判断两个顶点是否已经连通。这一点我们可以使用深度优先搜索或者广度优先搜索来解决,但这样的效率很低。
我们有更好的选择—并查集。将所有的顶点放入一个并查集中,判断两个顶点是否连通,只需要判断两个顶点是否在一个集合中或者说是否拥有共同的祖先即可。这样时间复杂度仅为O(logN)。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdio>

using namespace std;

#define maxn 1000001

struct edge
{
    int u;
    int v;
    int w;
};

struct edge e[maxn];
int n,m;
int f[maxn]={0},sum=0,cnt=0;
bool flag = false;

void quicksort(int left,int right)
{
    int i,j;
    struct edge t;
    if(left>right)
        return;

    i=left;
    j=right;

    while(i!=j)
    {
        while(e[j].w >= e[left].w && i<j)
            j--;
        while(e[i].w <= e[left].w && i<j)
            i++;

        if(i<j)
        {
            t=e[i];
            e[i]=e[j];
            e[j]=t;
        }
    }

    t=e[left];
    e[left]=e[i];
    e[i]=t;

    quicksort(left,i-1);
    quicksort(i+1,right);

    return;
}

int getf(int v)
{
    if(f[v]==v)
        return v;
    else
    {
        f[v]=getf(f[v]);
        return f[v];
    }
}

int merge(int v,int u)
{
    int t1,t2;

    t1=getf(v);
    t2=getf(u);

    if(t1!=t2)
    {
        f[t2]=t1;
        return 1;
    }

    return 0;
}

int main(int argc, char const *argv[])
{
    int i;

    scanf("%d %d",&n,&m);

    for(i=1;i<=m;i++)
        scanf("%d %d %d",&e[i].u,&e[i].v,&e[i].w);

    quicksort(1,m);

    sum=e[1].w;

    for(i=1;i<=n;i++)
        f[i]=i;

    for(i=1;i<=m;i++)
    {
        if(merge(e[i].u,e[i].v))
        {
            cnt=cnt+1;
            if(e[i].w>=sum)
                sum=e[i].w;
        }
        if(cnt==n-1)
        {
            flag=true;
            break;
        }
    }

    if(flag)
    {
        printf("%d",sum);		
    }
    else
    {
        printf("-1");
    }


    getchar();getchar();
    return 0;
}

如果小伙伴们不是很熟悉并查集,可以看一下我的另外一篇博客:
并查集

学会了就可以尝试一下,下面这道小题:
P3367 【模板】并查集

祝各位小伙伴学的开心,早日成为大牛,在奔向自己的理想的路上也要多看看沿途的风景。

新手上路,希望大家多多指教,多多交流,多多批评。

我是Mario,一个立志要考进MIT的程序猿。

发布了15 篇原创文章 · 获赞 12 · 访问量 1427

猜你喜欢

转载自blog.csdn.net/qq_41616301/article/details/89042843