最小生成树
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%20% 的数据,N≤5,M≤20。
对于 40%40% 的数据,N≤50,M≤2500。
对于 70%70% 的数据,N≤500,M≤10^4。
对于 100%100% 的数据:1≤N≤5000,1≤M≤2×10^5,1≤Zi≤10^4。
样例解释:
![](https://img-blog.csdnimg.cn/img_convert/b3b4855e487103691b17346712e50727.png)
所以最小生成树的总边权为2+2+3=7。
思路
放假之后连电脑都没有打开过,看到这个题目一脸懵。但是在啊哈算法书上看到了差不多的题目,尝试解析一下。有N个结点,M个供选择的道路,一个联通的无向图是不包含回路的,所以可以采用并查集解决问题。剩下的问题就是怎么找出最优解。
当然是排序从最小的边开始找了。
1 2 2
1 3 2
1 4 3
3 4 3
2 3 4
![](https://img-blog.csdnimg.cn/img_convert/33685e1565b41e8f581cb64a8b2ad7b3.png)
![](https://img-blog.csdnimg.cn/img_convert/153f844204c6f650426df6740297fda5.png)
![](https://img-blog.csdnimg.cn/img_convert/c9c5a7c01170c6587a5ba1e3107b8ec6.png)
这个时候所有的结点都已经链接在一起,最小的权重就是2+2+3=7
还要考虑不能连接所有结点的情况,输出orz。
代码如下
#include<stdio.h>
struct edge
{
int u;
int v;
int w;
};//结构体方便排序
struct edge e[200005];
int N,M;
int f[5005]={0},sum=0,count=0;
void qsort(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;
qsort(left,i-1);
qsort(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()
{
scanf("%d %d",&N,&M);
for(int i=1;i<=M;i++)
scanf("%d %d %d",&e[i].u,&e[i].v,&e[i].w);
qsort(1,M);//排序
for(int i=1;i<=N;i++)
f[i]=i;//并查集初始化
for(int i=1;i<=M;i++)
{
//判断两个顶点是否联通
if(merge(e[i].u,e[i].v))
{
count++;
sum+=e[i].w;
}
if(count==N-1)
{
printf("%d\n",sum);
break;
}
}
if(count!=N-1)
printf("orz\n");
}