最小生成树

来源:互联网 发布:java执行git命令 编辑:程序博客网 时间:2024/06/18 06:28

最小生成树即在一个n点的图里选出n-1条边使其边权和最小


题目:农场光纤 http://www.gdfzoj.com/oj/contest/150/problems/14


方法一:Prim算法(略暴力)

算法说明:循环以下步骤:

1.把已经选定的点 向外拓展一条最小权值的边(与已选点相连,由dis[]选出),把新边的点选定

2.更新选定的点到其余点的最小距离dis[]

详见程序:

#include <cstdio>#include <algorithm>#include <cstdlib>#include <cstring>using namespace std;const int maxValue=0x7fffffff;int a[105][105],dis[105],vis[105];int main(){    int i,sum=0,tot,j,n,min1;       freopen("a.txt","r",stdin);    scanf("%d",&n);       memset(vis,0,sizeof(vis));    memset(dis,0,sizeof(dis));    for (i=1;i<=n;i++)    {        for (j=1;j<=n;j++)        {            scanf("%d",&a[i][j]);//此题按邻接矩阵输入            if (i==1)//把1点选入              dis[j]=min(maxValue,a[i][j]);//dis[]记录已选点到其余点的最小值(分别)        }    }    vis[1]=1;    tot=1;//记录已选点数    while (tot<n)    {        min1=maxValue;        for (i=1;i<=n;i++)        {            if (vis[i]==0 && dis[i]<min1)//找最小边            {                min1=dis[i];                j=i;            }        }        tot++;        vis[j]=1;        sum+=dis[j];        for (i=1;i<=n;i++)        {            if (vis[i]==0 && dis[i]>a[j][i])//更新dis[]                dis[i]=a[j][i];        }    }    printf("%d",sum);       return 0;}

方法二:Kruskal算法(效率高)

算法说明:用边集存储。。。(此题邻接矩阵转结构体),并按边权大小排序

从最小边开始选

1.如果两个点不在同一个点集,把两个点对应的点集合并(并查集,数组记住父亲即可)

2.否则最小边下标++

找满n-1条边停止

程序coming:

#include <cstdio>#include <algorithm>using namespace std;struct node{int begin,end,w;};int n,num=0;int a[105][105];node edge[100*100+5];int tree[105];bool cmp(node x,node y){return x.w<y.w;}int find(int x)//并查集查找根结点{int i,fa,temp;if (tree[x]==x)return x;elsefa=x;while (fa!=tree[fa])fa=tree[fa];while (x!=fa){temp=tree[x];tree[x]=fa;x=temp;}return x;}void kruskal(){int i,sum=0,k,j,x,y,fx,fy;sort(edge,edge+num,cmp);k=1;//记录已选边数j=0;//已排序的边集下标 while (k<n) {x=edge[j].begin;y=edge[j].end;fx=find(x);fy=find(y); if (fx!=fy)//用并查集合并 {k++;//已选点++ sum+=edge[j].w;tree[fx]=tree[fy];//合并 PS:是根结点!!! }j++;//边:“下一个!” }printf("%d",sum);return ;}int main(){int i,x,j;//freopen("a.txt","r",stdin);//freopen("my.txt","w",stdout);scanf("%d",&n);for (i=1;i<=n;i++){tree[i]=i;//并查集树的父亲赋初值 for (j=1;j<=n;j++){scanf("%d",&a[i][j]);if (i<j && a[i][j]!=0)//产生边集edge[] {edge[num].begin=i;edge[num].end=j;edge[num].w=a[i][j];num++;}}}kruskal();return 0;}