普里姆算法
这是一个有感情的算法,十分的精巧和可爱,哈哈,我是这样感觉的。
要了解什么是普里姆,你需要一丢丢的图论和树的知识。
①明白普里姆的使用目的,就是构造一棵最小生成树。那么什么是最小生成树呢,顾名思义,最小生成树的概念是建立在生成树的概念上的,也就是说,你得明白什么是生成树。生成树是指包含一个图种所有的N个点,但是只含有N-1条边,也就是这个图的最小连通子图。如果你构成了一个生成树,那么你捏住其中一个点,可以将这个图中所有的点拉起来,同时你捏住的点就是这颗树的根节点,是不是很神奇。
②有了生成树的概念那么你就大概知道最小生成树的概念了,那就是使得链接所有的点的边的权值最小,那么这里什么是权值呢,题主一开始看见权值也是一脸懵逼啊,其实很简单,其实就是边的长度或者这条边代表的意义,比如你这条边如果指的是一条路,那么权值可以是长度也可以是修路的费用。恩,最小生成树的概念就出来了呀。那么你要怎么求出来呢。
③哒哒哒,神奇的普里姆算法就来了。普里姆在一定意义上讲是一种贪心的思想。你想想看,要使你取的所有边都是最小的,那么你要做的就是每次都取最小的边啊,对不对。没错就是这样,看图
看到图了不,一开始你可以任意取得一个点,这里假设是V1点,V1的相连着3条边,那么你要选择那一条呢,就想起上面说过的,我们要贪心,那就选择最短的,就是 1 ,好的,选择了这条边的结果是使得V1和V3相连了,那我们是不是接着判断V3的所有边的最短的呢,先等等,我们是不是忘记了什么,是啊,你们把V1忘记了,所以当你链接一个点的时候要判断的是连接起来的部分和剩下的部分最短的路径,所以这里就要时时的更新你的最短路径。这样就不会有其他的错误了吧。没错,你要做的就是重复上面的操作啦,只要重复N-1次,为什么呢,这要结合具体程序讲解。
来设置数组map[100][100] //这是用来存图的数组,存图有多种方式,邻接矩阵,邻接表等这里先不讲。
还需要一个最短路径的存储数组 dis[100] //这个其实主要用于更新路径的
最后还需要一个标记数组 visit[100] //标记已经被链接的点
好的接下来是代码:
#define INF 1e9+7
int Prim(int num,int *a)
{
int dis[100]; //路径数组的设置
int visit[100]={0}; // 标志数组的设置
int sum=0,min,min_i;
for(int i=0;i<num;i++) //将路径数组进行初始化
{
dis[i]=a[0][i];
}
visit[0]=1;
for(o=1;o<num;o++) //主体算法的实现
{
min=INF;
for(i=0;i<num;i++)
{
if(visit[i]==0&&min>dis[i])
{
min=dis[i];
min_i=i;
}
}
if(min==INF)
break;
sum+=min;
visit[min_i]=1;
for(i=0;i<num;i++) //路径的更新
{
{
if(visit[i]==0&&dis[i]>a[min_i][i])
{
dis[i]=a[min_i][i];
}
}
}
}
这个结果的展示结合具体题目http://acm.hdu.edu.cn/showproblem.php?pid=1102
杭电1102题
题意是要你实现这样的程序,给你一个N,这是所给村庄的数目,然后是一个邻接矩阵,这个矩阵会给你村庄之间的路径长度,同时还会输入一个M,这是已经修好的路,会告诉你是那两个村庄之间的。要你求解,使得每一个村庄之间都相连所要修的最短路径。
这显然是一个求最短路径的问题,那么你是不是想到了我们的普里姆呢,是不是很合适啊,最小生成树一套,完美啊。
那么看代码:
#include<cstdio>
#include<cstring>
#include<stdlib.h>
#include<algorithm>
#include<iostream>
#define INF 1e9+7
using namespace std;
int main()
{
int i,j,k,m,n,o,p,sum,imin,imin_i,q;
int a[100][100],s[100],dis[100];
int x,y,z,u;
while(scanf("%d",&k)==1)
{
memset(s,0,sizeof(s));
memset(dis,0,sizeof(dis));
sum=x=y=z=0;
imin=10000;
for(i=0;i<k;i++)
{
for(j=0;j<k;j++)
{
scanf("%d",&a[i][j]);
}
}
scanf("%d",&m);
for(i=0;i<m;i++)
{
scanf("%d%d",&p,&q);
a[p-1][q-1]=0;
a[q-1][p-1]=0;
s[p-1]=0;
s[q-1]=0;
}
s[0]=1;
for(i=0;i<k;i++)
{
dis[i]=a[0][i];
}
for(o=1;o<k;o++)
{
u=INF;
for(i=0;i<k;i++)
{
if(s[i]==0&&u>dis[i])
{
u=dis[i];
y=i;
}
}
if(u==INF)
break;
sum+=u;
s[y]=1;
for(i=0;i<k;i++)
{
if(s[i]==0&&dis[i]>a[y][i])
dis[i]=a[y][i];
}
}
printf("%d\n",sum);
}
return 0;
}
哒哒哒,完成。