目的:为了让无向图所有的点联通,且长度各边长度之和最小。
关键:图中不应存在回路,图中只需要n-1条边,n为点的数量。
最小生成树的两种方法:Prim和Kruskal。
P3366 【模板】最小生成树https://www.luogu.com.cn/problem/P3366
题目描述
如题,给出一个无向图,求出最小生成树,如果该图不连通,则输出 orz
。
输入格式
第一行包含两个整数N,M,表示该图共有 N 个结点和 M 条无向边。
接下来 M 行每行包含三个整数Xi,Yi,Zi,表示有一条长度为 Zi 的无向边连接结点 Xi,Yi。
输出格式
如果该图连通,则输出一个整数表示最小生成树的各边的长度之和。如果该图不连通则输出 orz
。
输入输出样例
输入 #1复制
4 5 1 2 2 1 3 2 1 4 3 2 3 4 3 4 3
输出 #1复制
7
说明/提示
数据规模:
对于 20%的数据,N≤5,M≤20。
对于 40%的数据,N≤50,M≤2500。
对于 70% 的数据,N≤500,M≤10^4。
对于 100% 的数据:1≤N≤5000,1≤M≤2×10^5,1≤Zi≤10^4。
样例解释:
所以最小生成树的总边权为 2+2+3=7。
kruskal就是排序加并查集。将所有的边排序,先选取较小的边,如果出现回路,则不选取。用并查集来判断是否出现回路。
Kruskal就像是往图中填边,只填n-1条边。
代码实现
#include<bits/stdc++.h>
using namespace std;
struct edge{
int x;
int y;
int z;
}e[200000+5];
void init(int *parent,int n)
{
for(int i=1;i<=n;i++)
{
parent[i]=i;
}
}
int find(int *parent,int x)
{
while(parent[x]!=x)
{
parent[x]=parent[parent[x]];
x=parent[x];
}
return x;
}
int link(int x,int y,int *parent)
{
int x_root=find(parent,x);
int y_root=find(parent,y);
if(x_root==y_root)
{
return 0;
}
else
{
parent[y_root]=x_root;
return 1;
}
}
void Quick_sort(int begin,int end)
{
if(begin>=end)
{
return;
}
int i=begin;int j=end;
while(i!=j)
{
while(e[j].z>=e[begin].z&&j>i)
{
j--;
}
while(e[i].z<=e[begin].z&&i<j)
{
i++;
}
edge temp=e[j];
e[j]=e[i];
e[i]=temp;
}
edge temp=e[begin];
e[begin]=e[i];
e[i]=temp;
Quick_sort(begin,i-1);
Quick_sort(i+1,end);
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
scanf("%d%d%d",&e[i].x,&e[i].y,&e[i].z);
}
int parent[5000+5];
init(parent,n);
Quick_sort(1,m);
int flag=0;
long long ans=0;
int cnt=0;
for(int i=1;i<=m;i++)
{
flag=link(e[i].x,e[i].y,parent);
if(flag)
{
ans+=e[i].z;
cnt++;
}
}
if(cnt!=n-1)
{
printf("orz");
return 0;
}
printf("%lld",ans);
return 0;
}
prim稍后再学