图的最小生成树
题目描述
现已知城镇地图如下,顶点是城镇编号,边上的值表示两地之间的路径。现在需要你通过全部城镇所需要的最小路径。
现在问题的关键:如何选出这n - 1条边,使得边的总长度最短呢?既然要求让边的总长度最短,我们自然而然就可以想到首先选择最短的边…直到选择了n - 1条边为止。这就需要我们首先对所有的边按照权值进行从小到大的排序,然后从最小的开始选,依次选择一条边,直到选择了n-1条边让整个图连通为止。
然后发现比较难实现的是如何判断改图是否已经连通,用并查集,将所有的顶点放入一个并查集中,判断两个顶点是否连通,只需判断该顶点是否在一个同集合中即可。
完整代码如下
#include<iostream>
using namespace std;
int n,m,f[1001];
struct edge{
int u;
int v;
int w;
};
edge e[1001];
void swap(edge *a,edge *b)
{
edge c = *a;
*a = *b;
*b = c;
}
void quick_sort(int left, int right)
{
int i = left,j = right;
int temp = e[left].w;
if(left >= right)
return;
while(i < j)
{
while(e[j].w > temp)
{
j --;
}
while(e[i].w < temp)
{
i ++;
}
swap(&e[i],&e[j]);
}
quick_sort(left,i - 1);
quick_sort(i + 1,right);
return;
}
int getf(int u)
{
if(f[u] == u)
return u;
else
{
f[u] = getf(f[u]);
return f[u];
}
}
int merge(int u, int v)
{
int t1,t2;
t1 = getf(u);
t2 = getf(v);
if(t1 != t2)
{
f[t2] = t1;
return 1;
}
return 0;
}
int main()
{
int sum = 0,num = 0;
cin >> n >> m;
for(int i = 1; i <= m; i ++)
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
quick_sort(1,m);
/*for(int i = 1; i <= m; i ++)
cout << e[i].u << " " << e[i].v<< " " << e[i].w << endl; */
//合并集初始化
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))
{
sum += e[i].w;
num ++;
}
if(num == n - 1)
break;
}
cout << sum;
return 0;
}
除了以上求最小生成树算法外,还有Prim算法。思想是先确定一个顶点,在这个顶点的基础上更新该生成树到一个非树顶点的距离。
核心代码:
int min = maxPath,j;
for(int i = 1; i <= n; i ++)
{
if(book[i] == 0 && dis[i] < min)
{
min = dis[i];
j = i;
}
}
book[j] = 1;
sum += dis[j];
//cout << sum;
count ++;
//扫描当前顶点j所有的边,更新生成树到一个非树顶点的距离
for(int i = 1; i <= n; i ++)
{
if(book[i] == 0 && dis[i] > e[j][i])
dis[i] = e[j][i];
}
全部代码:
#include<iostream>
#define maxPath 99999999
using namespace std;
int n,m;
int e[1001][1001];
int book[1001];
int dis[1001];
int main()
{
int sum = 0,num = 0,count = 0;
cin >> n >> m;
for(int i = 1; i <= n; i ++)
for(int j = 1; j <= n; j ++)
if(i == j)
e[i][j] = 0;
else
e[i][j] = maxPath;
for(int i = 1; i <= m; i ++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
e[u][v] = w;
e[v][u] = w;
}
for(int i = 1; i <= n; i ++)
dis[i] = e[1][i];
book[1] = 1;
count ++;
while(count < n)
{
int min = maxPath,j;
for(int i = 1; i <= n; i ++)
{
if(book[i] == 0 && dis[i] < min)
{
min = dis[i];
j = i;
}
}
book[j] = 1;
sum += dis[j];
//cout << sum;
count ++;
//扫描当前顶点j所有的边,更新生成树到一个非树顶点的距离
for(int i = 1; i <= n; i ++)
{
if(book[i] == 0 && dis[i] > e[j][i])
dis[i] = e[j][i];
}
}
cout << sum;
return 0;
}