最小生成树之Prim算法的学习心得与个人总结
最小生成树:MST
从4.24到今天5.4(除去29,30,1三天假期),完成了hdu1162 , hdu1102 , hdu3371 , hdu1233 , hdu1879 , hdu1301 , hdu1864 。由于我的入手学习方法错了,导致花的时间比较长,效率低。错误方法是看完白书和黑书直接做模板题,导致了对算法细节的理解一塌糊涂。建议应该先看各种博文,理解模板每一步的意义,再做题。此处感谢TZL对我及时的批评和建议,受用无穷。
现在谈一下最小生成树之Prim算法。这个算法是对图中的点进行贪心,从起始点开始,到图中个点的边权值最小的纳入最小生成树集合中的过程。算法中涉及到了一个二维数组和两个一维数组,其中这两个一维数组意义重大,曾一度搞得我把算法改的一塌糊涂。
二维数组map[ a ][ b ] :存储从点a到点b的边的权值。
一维数组low[ i ] :存储记录顶点集合T'中各顶点距离到顶点集合T中最小的边的权值。这时,从起始点与for循环中到达的点i一起进行贪心,与起始点和i点相连的路径中权值最小将存进low[i]。我一开始理解成当前点所连接的边的最小权值。这是不全面的。
一维数组v[i] :记录顶点集合T'中各顶点距离到顶点集合T中哪个顶点距离最近。类型可以选择int 或者bool 。对应的值时0,1 或者true,false 。当v[i] = 1 / true 时,表示点i属于集合T 。
根据个人习惯或者题目要求,一般选择从点1或者点0开始贪心。这时对应的边界值,初始化都是不同的。一定要上下对应!
Prim模板:(从点1开始贪心)
以 Hdu1863 - 通畅工程 为例题,这是一道MST的裸题。
题意:n为路的数目,m为村庄数;若无法连通,则输出-1,否则输出最小权值;当n为0时,直接结束不用输出结果。
代码:
#include
#include
#include
const int max = 0x7ffffff;
int map[105][105], v[105], low[105];
int n,m;
int MinTree(int n)
{
int min,flag,i,j,sum;
//初始化
memset(v, 0, sizeof(v));
memset(low, 0, sizeof(low));
v[1] = 1; //从村庄1开始贪心
sum = 0;
for(i=2; i<=m; i++) //此处的范围应是m村庄数,i为2~m中的一个村庄
{
low[i] = map[1][i]; //因为从点1开始贪心,所以是map[1][i]
}
for(i=2; i<=m; i++)
//此处的范围应是m村庄数;如果是n边数的话,当最小生成树已经构建完的话,而边数小于村庄数,
//则for会一直执行,而if则不成立,结果就是return -1.
{
min = max;
flag = -1;
//将与点1连通的边中权值最小的边找到
for(j=1; j<=m; j++)
{
if(!v[j] && low[j]map[flag][j])
{
low[j] = map[flag][j];
}
}
}
return sum; //返回最小值的和
}
int main()
{
int i,j,a,b,c,ans;
while(scanf("%d%d",&n,&m),n)
{
//初始化
for(i=1; i<=m; i++)
for(j=1; j<=m; j++)
{
if(i==j) map[i][j] = 0;
else map[i][j] = max;
}
//从点a到点b的权值是c
for(i=1; i<=n; i++)
{
scanf("%d%d%d",&a,&b,&c);
if(c < map[a][b])
{
map[a][b] = c;
map[b][a] = c;
}
}
ans = MinTree(n);
if(ans<=0) printf("?\n");
else printf("%d\n", ans);
}
return 0;
}