Description
给定结点数为n,边数为m的带权无向连通图G,所有结点编号为1,2,3....n。 求图G的最小生成树的边权和。输入格式
第一行两个正整数n和m。n,m<=2000 之后的m行,每行三个正整数a,b,w,描述一条连接结点a和b,边权为w的边。1=<a,b<=n,w<=10^18。 注意可能存在重边和自环。输出格式
一个整数表示图G的最小生成树的边权和(注意用长整型)。输入样例
7 12 1 2 9 1 5 2 1 6 3 2 3 5 2 6 7 3 4 6 3 7 3 4 5 6 4 7 2 5 6 3 5 7 6 6 7 1输出样例
16
普里姆算法查找最小生成树的过程,采用了贪心算法的思想。对于包含 N 个顶点的连通网,普里姆算法每次从连通网中找出一个权值最小的边,这样的操作重复 N-1 次,由 N-1 条权值最小的边组成的生成树就是最小生成树。
很好理解的其实!对着代码敲一敲,再在纸上画一画就没问题啦!
邻接矩阵e[2005][2005];标记数组v[2005]用于标记这个点是否走过;距离数组d[2005]用于记录到该点的最短距离,每一次循环需要更新;ans是所求的最短路径
memset(d,127/3,sizeof d);旨在给距离数组d[]赋一个很大的值,我们从顶点1开始走,因此先把v[1]标记上。函数long long prim()中第一个for循环求的是从顶点1到其它点的距离,然后调用GetMin函数,找到距离数组d[]中最小的值,返回其下标,同时将该点做上标记。并把其权值加在ans里。然后!更新距离数组d[]!
以输入样例为例,得到这样一个无向图。
第一个for循环得到的d数组和v数组如下。
第一次调用GetMin函数,发现从1到5的距离最短,第一次调用返回下标5,表示我们从1走到了5,为了避免以后再次走到5,我们把5这个点做上标记。现在走的总距离为ans=d[5]=2。我们现在在5这个位置,那么我们需要看看从5出发到其它点的距离会不会比从1出发到相同点的距离更小,所以每一次调用函数后需要更新d数组,如下。
再次调用GetMin函数,发现从5到6(也可以认为是从1到6)的距离最短,返回下标6,并做上标记,此时走的总距离为ans+=d[6]得到ans=5。同上,更新数组d,来看看如果从6出发的话,会不会有更短的路径。
不难发现,从6到7的路径最短,返回下标7,并做上标记,此时的总距离为ans=ans+d[7]=5+1=6。更新数组d。
从7到4的路径最短,返回下标4,并做上标记,此时的总距离为ans=ans+d[4]=6+2=8。更新数组d。
在这时,我们发现,从4出发的话,只能去到3(因为5已经走过了),而从4到3不如从7到3近,所以我们从7到3,返回下标3,并做上标记,此时的ans=8+d[3]=11。再次更新数组d。
只剩下一个2了,所以这道题的最短路径为ans=11+5=16。
最小生成树的样子是这样的
其实最小生成树这道题我说的有点啰嗦了,可以不用管从哪个点到哪个点的(但是管了也没事),每次只找最小的路径权值即可,因为最小生成树和最短路径的写法很像,写最短路径的时候就需要知道从哪个点出发到哪个点,再从新的点出发……
下面是全部代码。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
long long n,m,v[2005],d[2005],e[2005][2005];
int GetMin()
{
int i,mini=0;//d[0]很大
for(i=1;i<=n;i++)
{
if(d[mini]>d[i] && v[i]==0)
mini=i;
}
return mini;
}
long long prim()
{
memset(d,127/3,sizeof d);
v[1]=1;
long long ans=0;
for(int i=1;i<=n;i++)
d[i]=e[1][i];//顶点1到其它点的距离
for(int j=1;j<n;j++)//最小生成树有n-1条边
{
int minnode=GetMin();
v[minnode]=1;//标记
ans+=d[minnode];
for(int i=1;i<=n;i++)
d[i]=min(d[i],e[minnode][i]);//更新最短距离
}
return ans;
}
int main()
{
long long i,x,y,z;
memset(e,127/3,sizeof e);
cin >> n >> m;
for(i=1;i<=m;i++)
{
cin >> x >> y >> z;
if(x==y)//防止自环
continue;
e[x][y]=e[y][x]=min(e[x][y],z);//防止重边
}
cout << prim();
return 0;
}
Tips.题目有提示说要防止自环和重边
自环——边 e 的两个端点相同,则 e 称为一个自环。
重边——两个完全相同的边,称作(一组)重边。(无向图的边都是重边
出现重边的时候,取最短的