Prim算法+Kruskal算法
来源:互联网 发布:只有我知未删减在线看 编辑:程序博客网 时间:2024/05/17 09:14
转自:http://www.cnblogs.com/Veegin/archive/2011/04/29/2032388.html
http://www.cnblogs.com/Veegin/archive/2011/04/29/2032423.html
Prim算法:
今天从志权师兄那里学会了最小生成树。所谓生成树,就是n个点之间连成n-1条边的图形。而最小生成树,就是权值(两点间直线的值)之和的最小值。
首先,要用二维数组记录点和权值。如上图所示无向图:
int map[7][7];
map[1][2]=map[2][1]=4;
map[1][3]=map[3][1]=2;
......
然后再求最小生成树。具体方法是:
1.先选取一个点作起始点,然后选择它邻近的权值最小的点(如果有多个与其相连的相同最小权值的点,随便选取一个)。如1作为起点。
visited[1]=1;
pos=1;
//用low[]数组不断刷新最小权值,low[i](0<i<=点数)的值为:i点到邻近点(未被标记)的最小距离。
low[1]=0; //起始点i到邻近点的最小距离为0
low[2]=map[pos][2]=4;
low[3]=map[pos][3]=2;
low[4]==map[pos][4]=3;
low[5]=map[pos][5]=MaxInt; //无法直达
low[6]=map[pos][6]=MaxInt;
2.再在伸延的点找与它邻近的两者权值最小的点。
//low[]以3作当前位置进行更新
visited[3]=1;
pos=3;
low[1]=0; //已标记,不更新
low[2]=map[1][2]=4; //比5小,不更新
low[3]=2; //已标记,不更新
low[4]=map[1][4]=3; //比1大,更新后为:low[4]=map[3][4]=1;
low[5]=map[1][5]=MaxInt;//无法直达,不更新
low[6]=map[1][6]=MaxInt;//比2大,更新后为:low[6]=map[3][6]=2;
3.如此类推...
所有权值相加就是最小生成树,其值为2+1+2+4+3=12。
代码如下:
#include <stdio.h>#include <string.h>#define MaxInt 0x3f3f3f3f#define N 110//创建map二维数组储存图表,low数组记录每2个点间最小权值,visited数组标记某点是否已访问int map[N][N],low[N],visited[N];int n; int prim(){ int i,j,pos,min,result=0; memset(visited,0,sizeof(visited));//从某点开始,分别标记和记录该点 visited[1]=1;pos=1;//第一次给low数组赋值 for(i=1;i<=n;i++) if(i!=pos) low[i]=map[pos][i];//再运行n-1次 for(i=1;i<n;i++) {//找出最小权值并记录位置 min=MaxInt; for(j=1;j<=n;j++) if(visited[j]==0&&min>low[j]) { min=low[j];pos=j; }//最小权值累加 result+=min;//标记该点 visited[pos]=1;//更新权值 for(j=1;j<=n;j++) if(visited[j]==0&&low[j]>map[pos][j]) low[j]=map[pos][j]; } return result;} int main(){ int i,v,j,ans; while(scanf("%d",&n)!=EOF) {//所有权值初始化为最大 memset(map,MaxInt,sizeof(map)); for(i=1;i<=n;i++) for(j=1;j<=n;j++) { scanf("%d",&v); map[i][j]=map[i][j]=v; } ans=prim(); printf("%d\n",ans); } return 0;}
Kruskal算法:
对于稀疏图来说,用Kruskal写最小生成树效率更好,加上并查集,可对其进行优化。
Kruskal算法的步骤:
1.对所有边进行从小到大的排序。
2.每次选一条边(最小的边),如果如果形成环,就不加入(u,v)中,否则加入。那么加入的(u,v)一定是最佳的。
并查集:
我们可以把每个连通分量看成一个集合,该集合包含了连通分量的所有点。而具体的连通方式无关紧要,好比集合中的元素没有先后顺序之分,只有“属于”与“不属于”的区别。图的所有连通分量可以用若干个不相交集合来表示。
而并查集的精妙之处在于用数来表示集合。如果把x的父结点保存在p[x]中(如果没有父亲,p[x]=x),则不难写出结点x所在树的递归程序:
find(int x) {return p[x]==x?x:p[x]=find(p[x]);}
意思是,如果p[x]=x,说明x本身就是树根,因此返回x;否则返回x的父亲p[x]所在树的根结点。
既然每棵树表示的只是一个集合,因此树的形态是无关紧要的,并不需要在“查找”操作之后保持树的形态不变,只要顺便把遍历过的结点都改成树根的儿子,下次查找就会快很多了。如下图所示:
#include <stdio.h>#include <stdlib.h>#include <algorithm>#define N 150using namespace std;int m,n,u[N],v[N],w[N],p[N],r[N];int cmp(const int i,const int j) {return w[i]<w[j];}int find(int x) {return p[x]==x?x:p[x]=find(p[x]);}int kruskal(){ int cou=0,x,y,i,ans=0; for(i=0;i<n;i++) p[i]=i; for(i=0;i<m;i++) r[i]=i; sort(r,r+m,cmp); for(i=0;i<m;i++) { int e=r[i];x=find(u[e]);y=find(v[e]); if(x!=y) {ans += w[e];p[x]=y;cou++;} } if(cou<n-1) ans=0; return ans;} int main(){ int i,ans; while(scanf("%d%d",&m,&n)!=EOF&&m) { for(i=0;i<m;i++) { scanf("%d%d%d",&u[i],&v[i],&w[i]); } ans=kruskal(); if(ans) printf("%d\n",ans); else printf("?\n",ans); } return 0;}
- Prim算法+Kruskal算法
- Prim算法 Kruskal算法
- Prim算法和Kruskal算法
- Prim算法和Kruskal算法
- Prim算法和Kruskal算法
- Prim算法和Kruskal算法
- Prim算法和Kruskal算法
- Prim算法和Kruskal算法
- Prim算法和Kruskal算法
- Prim算法和Kruskal算法
- Prim算法和Kruskal算法
- Prim算法 Kruskal算法 简述
- Prim算法和Kruskal算法
- prim 算法和 kruskal算法
- Prim算法和Kruskal算法
- Prim算法和Kruskal算法
- poj1287-Kruskal和prim算法。
- Prim、Kruskal、Prim+Heap算法效率实测
- [LeetCode] Subsets
- Lua 常用库
- hdu 3714 Error Curves 2010年成都赛区
- Web应用程序项目XX已配置为使用IIS
- 韩信点兵背后的思想
- Prim算法+Kruskal算法
- leetcode第一刷_Two sum
- ACM数论总结
- 使用share SDK实现QQ空间授权登录、分享(1.x版本)
- 浅谈如何给自己的网站定位?
- AMD盯准高端平板
- 比较有意思找最小值
- 框架设计原则
- HDU 1856 More is better (裸的并查集,记录下秩就可以了)