例题:最短网络 图论算法之最小生成树 prim//kruskal 学习笔记

来源:互联网 发布:电脑检测软件 知乎 编辑:程序博客网 时间:2024/06/12 15:40

图论算法之最小生成树  prim//kruskal

        最小生成树简单的说就是在一个图里选取一些边,使这些边以及它们所连接的结点组成一棵树(两两结点之间可以到达),并且使选取的边的边权最小。
        它的成立条件是图是连通的。并且选取的边数为n-1。(有n个结点,n-1条边,只能为一棵树,没有别的可能)
        主要有两个算法:prim和kruskal

prim

【思路】

        prim算法写起来和dijkstra很像,因为他们的大体思路是一样的。s[i]表示与i相连的所有边的最小值(条件是这条边的另一端点已经是之前扩展过的)。每一次找到s值最小的点,用它去扩展和他相连的所有的边的另一结点,经过n-1此操作,就可以完成对所有点和边的扩展。

【代码】

#include<iostream>#include<cstdio>#include<cstring>using namespace std;int n,i,j,Min,minj,tot;int a[105][105],s[105];bool b[105];int main(){scanf("%d",&n);for (i=1;i<=n;++i)  for (j=1;j<=n;++j)    scanf("%d",&a[i][j]);for (i=1;i<=n;++i)  s[i]=0x7777777;s[1]=0;for (i=1;i<=n-1;++i){Min=0x7777777;for (j=1;j<=n;++j)  if (!b[j]&&s[j]<Min)  {  Min=s[j];  minj=j;  }b[minj]=true;for (j=1;j<=n;++j)  if (!b[j]&&a[minj][j]<s[j])    s[j]=a[minj][j];}for (i=1;i<=n;++i)  tot+=s[i];printf("%d",tot);return 0;}

【求出最小生成树中的所有边】

也不是很难想,只要再加一个前驱就行了,最后递归输出;

【代码】

#include<iostream>#include<cstdio>#include<cstring>using namespace std;int n,i,j,Min,minj,tot;int a[105][105],s[105],pre[105];bool b[105];void print(int x){if (pre[x]==0){printf("%d\n",x);return;}print(pre[x]);printf("%d\n",x);return;}int main(){scanf("%d",&n);for (i=1;i<=n;++i)  for (j=1;j<=n;++j)    scanf("%d",&a[i][j]);for (i=1;i<=n;++i)  s[i]=0x7777777;s[1]=0;for (i=1;i<=n-1;++i){Min=0x7777777;for (j=1;j<=n;++j)  if (!b[j]&&s[j]<Min)  {  Min=s[j];  minj=j;  }b[minj]=true;for (j=1;j<=n;++j)  if (!b[j]&&a[minj][j]<s[j])  {  s[j]=a[minj][j];  pre[j]=minj;  }}for (i=1;i<=n;++i)  tot+=s[i];printf("%d\n",tot);print(n);return 0;}

例:最优布线问题

【问题描述】
  学校有n台计算机,为了方便数据传输,现要将它们用数据线连接起来。两台计算机被连接是指它们间有数据线连接。由于计算机所处的位置不同,因此不同的两台计算机的连接费用往往是不同的。
    当然,如果将任意两台计算机都用数据线连接,费用将是相当庞大的。为了节省费用,我们采用数据的间接传输手段,即一台计算机可以间接的通过若干台计算机(作为中转)来实现与另一台计算机的连接。
  现在由你负责连接这些计算机,任务是使任意两台计算机都连通(不管是直接的或间接的)。
【输入格式】
  输入文件wire.in,第一行为整数n(2<=n<=100),表示计算机的数目。此后的n行,每行n个整数。第x+1行y列的整数表示直接连接第x台计算机和第y台计算机的费用。
【输出格式】
  输出文件wire.out,一个整数,表示最小的连接费用。
【输入样例】
  3
  0 1 2
  1 0 1
  2 1 0
【输出样例】
    2   (注:表示连接1和2,2和3,费用为2)

【解题思路】
prim裸题,直接用上面给的标算都能过。。在此就不粘代码了->_->

kruskal

【算法描述】

需要用到并查集,具体过程如下:
1.初始化并查集。father[x]=x。
2.tot=0
3.将所有边用快排从小到大排序。
4.计数器 k=0;
5.for (i=1; i<=M; i++)      //循环所有已从小到大排序的边
  if  这是一条u,v不属于同一集合的边(u,v)(因为已经排序,所以必为最小),
    begin
     ①合并u,v所在的集合,相当于把边(u,v)加入最小生成树。
     ②tot=tot+W(u,v)
      ③k++
      ④如果k=n-1,说明最小生成树已经生成,则break;
    end;
6. 结束,tot即为最小生成树的总权值之和。

【代码】

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>using namespace std;struct hp{int x,y,s;}a[10005];int n,i,j,dis,num,tot,k;int father[10005];int cmp(hp a,hp b){return a.s<b.s;}int find(int x){if (x==father[x]) return father[x];father[x]=find(father[x]);return father[x];}void merge(int x,int y){int f1=find(x);int f2=find(y);father[f1]=f2;return;}int main(){scanf("%d",&n);for (i=1;i<=n;++i)  for (j=1;j<=n;++j)  {  scanf("%d",&dis);  if (dis!=0)  {  num++;  a[num].x=i;  a[num].y=j;  a[num].s=dis;  }  }for (i=1;i<=n;++i)  father[i]=i;sort(a+1,a+num+1,cmp);for (i=1;i<=num;++i){if (find(a[i].x)!=find(a[i].y)){merge(a[i].x,a[i].y);tot+=a[i].s;k++;}if (k==n-1) break;}printf("%d",tot);return 0;}

【记录路径】

其实并没有什么好说的,加一个bool或者别的什么记录一下,最后按顺序输出即可;

例:最短网络

【问题描述】
  农民约翰被选为他们镇的镇长!他其中一个竞选承诺就是在镇上建立起互联网,并连接到所有的农场。当然,他需要你的帮助。约翰已经给他的农场安排了一条高速的网络线路,他想把这条线路共享给其他农场。为了用最小的消费,他想铺设最短的光纤去连接所有的农场。你将得到一份各农场之间连接费用的列表,你必须找出能连接所有农场并所用光纤最短的方案。每两个农场间的距离不会超过100000。
【输入格式】
第一行:
农场的个数,N(3<=N<=100)。
第二行..结尾
后来的行包含了一个N*N的矩阵,表示每个农场之间的距离。理论上,他们是N行,每行由N个用空格分隔的数组成,实际上,他们限制在80个字符,因此,某些行会紧接着另一些行。当然,对角线将会是0,因为不会有线路从第i个农场到它本身。
【输出格式】
    只有一个输出,其中包含连接到每个农场的光纤的最小长度。
【输入样例】agrinet.in
    4
    0  4  9  21
    4  0  8  17
    9  8  0  16
    21 17 16  0
【输出样例】agrinet.out
28

【解题思路】
kruskal裸题。。。依然是用标算就能过,,,我闲的没事粘上来。。。<-_<-
0 0