最小生成树

 

预备知识:

1.树:(其实只需要记住2叉树的特性,其详细请见“关于二叉树”)

2.并查集(记得住getfather()函数怎么写就行,其详细请见“关于并查集”)

引例:村长的难题

      小明是信竞村的村长,小明打算给该村的所有人家都连上网。
      该村有n(1<=n<=1000)户人家,编号1到n。由于地形等原因,只有m(1<=m<=50000)对人家之间可以相互牵线。在不同人家间牵线的长度不一定相同。比如在Ai与Bi之间牵线需要Ci米长的网线。
      整个村的网络入口在1号人家,小明的问题是:是否能使得所有人家都连上网?使所有人家都连上网,最少需要多少米网线?

此问题可以把村庄看成一个图,其中村子为点,网线为边。

解题主要思路:

    1.用网线连接n户人家。

     2.找出一种方案,使得总的长度最少。

(无向图)

前方高能

两种算法,打遍天下

Prim

Kruskal

1.Kruskal

Kruskal算法基本思想:

   1.每次选不属于同一生成树的且权值最小的边的顶点,将边加入生成树,并将所在的2个生成树合并,直到只剩一个生成树

   2.排序使用sort

   3.检查是否在同一生成树用并查集

   ####总复杂度O(mlogm)

设初值代码见下:

#define maxn 1001
#define  maxe 10001//这两个其实可以不设,根据题目给出的大小设数组就行了//1 
struct node 
{
       int a, b; //边的2个顶点(a为起点,b为终点) 
       int len; //边的长度
};
node Edge[maxe];    //保存所有边的信息//1 
int Father[maxn]    //Father存i的父亲节点//1 
int n, m;            //n为顶点数,m为边数

 

cmp与初始化函数见下:

bool cmp(node a, node b) //按边长由小到大排序
{
       return a.len < b.len;//n为顶点数,m为边数
}
void init()//初始化
{
   scanf("%d %d", &n, &m);
   for(int i = 1; i <= m; i++){
		scanf("%d %d %d", &Edge[i].a, Edge[i].b, Edge[i].len); //读入图的信息(根据题意,不要生搬)	
	}
   for(int i = 1; i <= n; i++){
		Father[i] = i; //初始化并查集
	}
  sort(Edge + 1, Edge + 1 + m, cmp);       //使用快速排序将边按权值从小到大排列(cmp见上)
}

getfather与Kruska与int main函数见下:

int main()
{
  init();//输入
  kruskal();//执行算法
   return 0;//完事
}
int getFather(int x)//用来判断2个顶点是否属于同一个生成树
{
  if(x != Father[x])
        Father[x]=getFather(Father[x]);
  return Father[x];
}
void kruskal() 
{
 int x, y, k, cnt, tot;     //k为当前边的编号,tot统计最小生成树的边权总和
                        //cnt统计进行了几次合并。n-1次合并后就得到最小生成树
 cnt = 0; k = 0; tot = 0;//赋初值
 while(cnt < n - 1)         //n个点构成的生成树总共只有n-1条边
 {
      k++;
        x = getFather(Edge[k].a);
        y = getFather(Edge[k].b);
      if(x != y)
      {
        Father[x] = y; //合并到一个生成树
        tot = tot + Edge[k].len;
        cnt++;
      }
 }
 printf("%d\n",tot);
}

2.Prim

1.任选一个点,加入生成树集合

 2.在未加入生成树的点中,找出离生成树距离最近的一个点,将其加入生成树。

3.反复执行2,直到所有点都加入了生成树

void prim(int x){//开始时任选一点x加入生成树,故一开始树中只有一个点x


      int  dis[101], path[101], i, j, k, Min;//dis记录各节点到生成树的最小距离//path[i]记录生成树中与节点i最近的一个节点的编号,用于记录路径


      for(i = 1;i <= n; i++)
      {   dis[i] = map[i][x];  path[i] = x;  }//初始化,将每个节点到生成树的最小距离赋值为它到点x的距离,将生成树中与i最近的节点赋值为x(因为此时生成树中只有一个节点x)

      for(i = 1;i <= n - 1; i++)//除x外,还有n-1个节点要讨论

     {
           Min = inf;//inf为自定义的一个表示无穷大的数。

           for(j = 1; j <= n; j += 1)//找出在未加入生成树的节点中,离当前生成树距离最近的一个节点

                if ( (dis[j] != 0) && (dis[j] < Min) )
                {   Min = dis[j];    k = j;    };
           dis[k] = 0;//将找出的离生成树距离最近的节点t到生成树的距离赋值为0,表示它已经加入到树中了

           for(j = 1; j <= n; j++)
                if(dis[j] > map[j][k])
                { dis[j] = map[j][k];  
                  path[j] = k; }//k加入生成树后,可能有其他节点到生成树的最短距离发生变化,调整他们的path值
//讨论未加入到生成树的节点j与k的距离是否比j原来到生成树的最短距离dis[j]要短,如果是,则用新的更短的距离取代原来的距离,并将生成树离j最近的节点改成t
 

    }
//输出最短路径总长度
for(i=1;i<=n;i++){
      if(path[i]!=i){
        total=total+map[i][path[i]]; 
        cout<

prim时间复杂度O(n2)

堆优化O(nlogn) //见迪杰斯特拉(最短路)的优化

 

拜拜!!!

你可能感兴趣的:(信奥)