最小生成树(MST)——模板题

P3366 【模板】最小生成树 题解

题目:

Description
给出一个无向网,求该无向网的最小生成树。各条边的权重不超过100000。
Input
输入的第一行是一个整数N,表示该网的顶点个数。 3 ≤ N ≤ 100
接下来是N行,每行N个整数,表示每个顶点到其余顶点的距离。
Output
输出该最小生成树的权重。
Sample Input
4
0 4 9 21
4 0 8 17
9 8 0 16
21 17 16 0
Sample Output
28

Kruskal:

先从小到大排序,再用并查集判断起始点是否联通。

先贴代码,具体内容待之后有空补充

#include
using namespace std;
int f[110],n;
struct node
{
    int u,v,dis;
}Node[11000];
int cmp(node s1,node s2)
{
    return s1.dis<s2.dis;
}
/*以下三个函数均为并查集的操作*/
void init()///f数组初始化
{
    for(int i=1;i<=n;i++){
        f[i]=i;
    }
}
int find_(int x)///找祖先
{
    if(f[x]==x)
        return x;
    else
        return f[x]=find_(f[x]);
}
int merge(int u,int v)///判断u和v当前是否在同一集合内(即是否联通),如果不是则进行合并操作
{
    int x,y;
    x=find_(u);
    y=find_(v);
    if(x!=y){
        f[y]=x;
        return 1;
    }
    return 0;
}
int main()
{
    ios::sync_with_stdio(false);
    int m=0,sum=0,x;
    cin>>n;
    init();
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cin>>x;
            if(i!=j)
                Node[m].u=i,Node[m].v=j,Node[m++].dis=x;
        }
    }

    sort(Node,Node+m,cmp);

    int k=0;
    for(int i=0;i<m;i++){
        if(merge(Node[i].u,Node[i].v)){
            sum+=Node[i].dis;
            k++;
        }
        if(k==n-1)
            break;
    }
    cout<<sum<<endl;
    return 0;
}

Prim

没有任何优化的做法:

#include
using namespace std;
const int inf=0x3f3f3f3f;
int n,G[110][110],dis[110],ans;
bool book[110];
void prim()
{
    for(int i=1;i<=n;i++){///以顶点1为跟点,dis数组中存储顶点1到其余顶点的距离
        dis[i]=G[1][i];
    }
    book[1]=1,dis[1]=0;///book数组标记顶点1已用过,
    int Min,k;
    for(int i=1;i<=n-1;i++){
        k=0,Min=inf;///初始化Min为无穷大
        for(int j=1;j<=n;j++){
            if(!book[j]&&dis[j]<Min){ ///寻找i到j的距离最小值
                Min=dis[j];
                k=j;
            }
        }
        ans+=dis[k];
        ///f(k==0)return;这句有没有都对,思考下这一句究竟有没有存在的作用
        book[k]=1;
        for(int u=1;u<=n;u++){
            if(!book[u]&&dis[u]>G[k][u])
                dis[u]=G[k][u];  ///维护dis数组
        }
    }
}
int main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cin>>G[i][j];
        }
    }
    prim();
    cout<<ans<<endl;
}

题目:

(special judge)Time Limit: 4000/2000 MS (Java/Others)
Description
给出一个无向图,求该图的最小生成树。
Input
多测试用例。

第一行:两个正整数 N 和 E ( 0 < N < 10000, N < E < 40000 ),分别表示该图的顶点个数、边的总数。顶点编号从 0~N-1

接下来E行,每行是3个整数:u v w,表示 顶点u 与 顶点v 之间有一条权值为w的边。 0 ≤ u ,v < N , 0 < w < 20

Output
每个测试用例:

如果该图不连通,输出一行: unconnected graph

否则输出n行:第1行是该生成树的边权之和,第2~n行以 u v w 的形式输出生成树的各条边,其中u和v表示这条边的两个顶点,w表示这条边的权重。生成树的边的输出次序不限,只要求不重复、不遗漏。 如:3 7 2 跟 7 3 2 是同一条边。

Sample Input
9 14
0 2 4
0 1 8
2 1 11
2 3 8
1 4 7
1 5 1
4 3 2
5 4 6
5 7 2
7 3 4
6 3 7
7 6 14
6 8 9
7 8 10

Sample Output
37
2 0 4
5 1 1
3 2 8
3 4 2
5 7 2
3 7 4
3 6 7
8 6 9

Kruskal做法:

耗时:1305MS

#include
#include
using namespace std;
struct node
{
    int u,v,edge;
}G[80008],T[10005];
int f[10005];
int cmp(node s1,node s2)
{
    return s1.edge<s2.edge;
}
void init(int n)
{
    for(int i=0;i<=n;i++){
        f[i]=i;
    }
}
int get(int x)
{
    if(f[x]==x)
        return x;
    return get(f[x]);
}
int merge(int u,int v)
{
    int t1,t2;
    t1=get(u);
    t2=get(v);
    if(t1!=t2){
        f[t2]=t1;
        return 1;
    }
    return 0;
}
int main()
{
    ios::sync_with_stdio(false);
    int N,E;
    while(!(cin>>N>>E).eof()){
        for(int i=1;i<=2*E;i+=2){
            cin>>G[i].u>>G[i].v>>G[i].edge;
            G[i+1].u=G[i].v,G[i+1].v=G[i].u,G[i+1].edge=G[i].edge;///无向图
        }
        init(N);///f数组初始化
        sort(G+1,G+2*E+1,cmp);///按边从小到大排
        int dis=0,k=0;
        for(int i=1;i<=2*E;i++){
            if(merge(G[i].u,G[i].v)){///u,v两点联通
                dis+=G[i].edge;
                T[k].u=G[i].u,T[k].v=G[i].v,T[k++].edge=G[i].edge;
            }
            if(k==N-1)break;
        }
        if(k<N-1)///该图不连通
            cout<<"unconnected graph"<<endl;
        else{
            cout<<dis<<endl;
            for(int j=0;j<k;j++){
                cout<<T[j].u<<' '<<T[j].v<<' '<<T[j].edge<<endl;
            }
        }
    }
    return 0;
}

从策略上来说,Prim算法是直接查找,多次寻找邻边的权重最小值,而Kruskal是需要先对权重排序后查找的~
所以说,Kruskal在算法效率上是比Prim快的,因为Kruskal只需一次对权重的排序就能找到最小生成树,而Prim算法需要多次对邻边排序才能找到~
prim:该算法的时间复杂度为O(n2)。与图中边数无关,该算法适合于稠密图。
kruskal:需要对图的边进行访问,所以克鲁斯卡尔算法的时间复杂度只和边又关系,可以证明其时间复杂度为O(eloge)。适合稀疏图

你可能感兴趣的:(算法)