洛谷 P1111 修复公路(最小生成树,Kruskal并查集的优势,以及Prim蓝白点的局限性)

P1111 修复公路

题目背景

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

题目描述

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

输入输出格式

输入格式:

第1行两个正整数N,M

下面M行,每行3个正整数x, y, t,告诉你这条公路连着x,y两个村庄,在时间t时能修复完成这条公路。

输出格式:

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

输入输出样例

输入样例#1:  复制
4 4
1 2 6
1 3 4
1 4 5
4 2 3
输出样例#1:  复制
5

说明

N<=1000,M<=100000

x<=N,y<=N,t<=100000



=========================================


前面分析有点乱,重点还是关注黑体部分以及后面的总结:


拿到题目,明显最小生成树,但应该用哪种解法,刚开始考虑Prim算法,但发现很多问题:

补充:该题所有道路可同时修复,所以要输出最小生成树中最大的那条边;

①、Prim算法的主要思想就是求解出每个点到最近白点的距离,然后根据这些最短距离连接起所有白点;

②、Pirm局限性:无法判断该图是否连通;判断图的连通性,我们可以想到如果最后去重后得到的边数>=n-1,则该图可构成最小生成树,而对于Prim算法,若不连通的图中有一部分形成环,则该条件不再试用。而对于Kruskal来说,见下面......

③、如果硬要做的话(无法摆脱Prim 的局限性):该题有不连通的两点,而且两点间可能有多条线路,所以对于图的构建来说,要有g[i][j]=g[j][i]=min(多条路中最短的一条,去掉耗时长的路),且g初始化为一个极大值。

而对于Kruskal来说,其核心思想是判断当前遍历的一条边是否可以加入到最小生成树中,能加入最小生成树的边,所连接的都是两个不连通的区域,即加入边的过程也是打通两个不连通区域的过程,所以如果生成树中的边数k能够到达n-1,则说明该图可连通(当前边可连接起n个孤立区域\点),如果到达不了n-1,说明该图不能连通。

另外在判断是否可入树时,我们要考虑该边所连两点是否有相同祖先,若没有,则需要合并两点(合并后具有共同祖先),这样也就能保证连接该两点的其他边不会再进入最小生成树中。

所以Prim的90分代码:

#include<bits/stdc++.h>
using namespace std;
int g[1001][1001];
bool flag[1001][1001];
bool u[1001];
int minn[1001];
int main()
{
    //freopen("1.txt","r",stdin);
    memset(g,0x7f,sizeof(g));//important;
    memset(minn,0x7f,sizeof(minn));
    memset(u,1,sizeof(u));
    int n,m,tm;
    cin>>n>>m;
    tm=m;//wrong
    if(tm<n-1)
    {
        cout<<"-1"<<endl;
        return 0;
    }
    for(int i=1;i<=m;++i)
    {
            int x,y,z;
            cin>>x>>y>>z;
            if(flag[x][y]==0||flag[y][x]==0)
            {
                flag[x][y]=1;
                flag[y][x]=1;
                g[x][y]=z;//wrong
                g[y][x]=z;
               // cout<<x<<" "<<y<<endl;

            }

            else
            {
                tm--;
                if(tm<n-1)
                {
                    cout<<"-1"<<endl;
                    return 0;
                }
                if(z<g[x][y])g[x][y]=z,g[y][x]=z;
            }

    }
    m=tm;
    //cout<<g[2][3]<<endl;
    //cout<<m<<endl;
    minn[1]=0;
    for(int i=1;i<=n;++i)
    {
        int k=0;
        for(int j=1;j<=n;++j)
            if(u[j]&&minn[j]<minn[k])k=j;
        u[k]=0;
        //cout<<"k="<<k<<endl;
        for(int j=1;j<=n;++j)
        {
            if(u[j]&&g[k][j]<minn[j])minn[j]=g[k][j];
        }

    }
    int ans=0;
    for(int i=1;i<=n;++i){
        if(minn[i]>ans)ans=minn[i];//wrong
        //cout<<minn[i]<<endl ;
    }
    cout<<ans<<endl;
    return 0;


}

Kruskal的AC代码:

#include<bits/stdc++.h>
using namespace std;
struct point
{
    int l,r,v;
}a[1000001];
int fat[1001];
bool cmp(point x,point y)
{
    return x.v<y.v;
}
int father(int x)
{
    while(x!=fat[x])x=fat[x];//非递归实现 查找 功能;
    return x;
}
void unionn(int x,int y)
{
    fat[father(x)]=father(y);
}
int main()
{
    //freopen("1.txt","r",stdin);
    int m,n;
    cin>>n>>m;
    for(int i=1;i<=m;++i)
    {
        int x,y,z;
        cin>>x>>y>>z;
        a[i].l=x;
        a[i].r=y;
        a[i].v=z;

    }
    for(int i=1;i<=n;++i)fat[i]=i;
    sort(a+1,a+1+m,cmp);
    int k=0,ans;
    bool flag=1;
    for(int i=1;i<=m;++i)
    {
        if(father(a[i].l)!=father(a[i].r))
        {
            k++;
            unionn(a[i].l,a[i].r);
        }
        if(k==n-1)
        {
            ans=a[i].v;
            flag=0;//判断图是否可连通;
            break;
        }
    }
    if(flag)cout<<"-1"<<endl;
    else cout<<ans<<endl;
    return 0;
}

最后总结:

    目前来看,最小生成树类题目的输入数据有三种给出形式:

1、一种是给出图中所有点的指向(有向图);(例如信息学P511页最优布线问题)

注意点:

    Prim算法中要将 邻接矩阵 初始化为极大值,以防止出现有不相连点的情况,同时注意不能构成最小生成树的情况(所以一般不用Prim吧);

    Kruskal:无注意点;每有一条单向边,总边数m+1。

2、另一种是只给出能相连的点之间的关系(和 1 类似);(如本题)

    Prim算法中上同......

    Kruskal:无注意点。

3、还有一种是给出n个孤立点(任意一点都能与该点外的任意一点相连)。

(如 洛谷 公路修建:https://www.luogu.org/problemnew/show/P1265

    Prim中初不初始化无所谓。

    Kruskal:无注意点。


(重点)综上所述:

        边数较多,用Prim,但需保证所给图能够生成最小生

成树(一般来说比如题目给出好多孤立点);边数较少,用

Kruscal,“一边一m++”,其他暂无注意点



The end;






猜你喜欢

转载自blog.csdn.net/qq_41661919/article/details/80204562