无向图最小生成树
N个点M条边的无向连通图,每条边有一个权值,求该图的最小生成树。
Input 第1行:2个数N,M中间用空格分隔,N为点的数量,M为边的数量。(2 <= N <= 1000, 1 <= M <= 50000)
第2 - M + 1行:每行3个数S E W,分别表示M条边的2个顶点及权值。(1 <= S, E <= N,1 <= W <= 10000) Output 输出最小生成树的所有边的权值之和。 Sample Input
9 14 1 2 4 2 3 8 3 4 7 4 5 9 5 6 10 6 7 2 7 8 1 8 9 7 2 8 11 3 9 2 7 9 6 3 6 4 4 6 14 1 8 8Sample Output
37
解题思路:
其实就是一道最小生成树的模板题,用Kruskal算法来写。将每条路的信息存储起来,并将边的权值按从小到大进行排序,每次选择权值最小的边加入树中,并把边的两个顶点加入到一个集合中,若这两个顶点本身就在一个集合中(即加入这条边就构成了回路),就舍弃这条边判断下一个。直到选取了n-1条边加入树中,最小生成树构造完成。具体看代码。
代码如下:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define INF 0x3f3f3f3f //定义极大值INF
using namespace std;
int n, m, sum, ans;
int f[1010]; //数组f判断点的集合状态
struct node
{
int x;
int y; //存储边的两个顶点以及权值
int z;
} Q[50010];
bool cmp( node a, node b )
{
return a.z < b.z; //将权值从小到大排列
}
int getf( int v )
{
if( v != f[v] )
f[v] = getf(f[v]); //查找父节点
return f[v];
}
int lian( int t1, int t2 )
{
int v1 = getf(t1);
int v2 = getf(t2);
if( v1 != v2 )
{ //若两个顶点不在同一集合中,这条边加入树
f[v1] = v2;
return 1;
}
return 0;
}
void Kruskal()
{
for( int i = 0; i < m; i++ )
{
if( lian(Q[i].x,Q[i].y) )
{
ans++;
sum = sum + Q[i].z;
if( ans == n-1 ) //当选取n-1条边时最小生成树构造完成
break;
}
}
}
int main()
{
while( ~scanf("%d%d",&n,&m) )
{
for( int i = 0; i < m; i++ )
scanf("%d%d%d",&Q[i].x,&Q[i].y,&Q[i].z);
sort(Q,Q+m,cmp);
sum = ans = 0;
for( int i = 1; i <= n; i++ )
f[i] = i; //初始化父节点都是本身
Kruskal();
printf("%d\n",sum);
}
return 0;
}