背景
今天遇到这样一道题:(洛谷2820)
某个局域网内有n(n<=100)台计算机,由于搭建局域网时工作人员的疏忽,现在局域网内的连接形成了回路,我们知道如果局域网形成回路那么数据将不停的在回路内传输,造成网络卡的现象。因为连接计算机的网线本身不同,所以有一些连线不是很畅通,我们用f(i,j)表示i,j之间连接的畅通程度,f(i,j)值越小表示i,j之间连接越通畅,f(i,j)为0表示i,j之间无网线连接。
输出Σf(i,j)的的最大值
是最小生成树没错了,于是去温习了一遍图论。。。
最小生成树定义(摘自百度百科):
其实就是给你一个有n个点的无向联通图,每条边有一个选择的费用, 你需要只选择其中的n-1条边(因为n个点的联通图要保证无环最多只能有n-1条边),使得这个图仍然联通,保证不构成环,且费用和最小。
以下面这张图为例:
![](https://images2018.cnblogs.com/blog/1443857/201808/1443857-20180823154925100-994621538.png)
它的最小生成树当然是:
![](https://images2018.cnblogs.com/blog/1443857/201808/1443857-20180823155013970-378429334.png)
实现最小生成树有两种算法,分别是Prim算法和Kruskal算法(因为还有蛋疼的开学考要准备,就先介绍一下Prim,过几天更Kruskal)
Prim算法
贪心策略:
1)把已经在树中的点全部装进集合A,其余装进集合B,这样就保证不构成环
2)每次选取连接A集合与B集合的边中的最短的一条进行连接,这样保证了边权和最小
实现方法:
1)随便找一个起始点(反正最后都在树上),并装进A集合
2)循环n-1次,其中每次:
1)选取连接A集合与B集合的边中的最短的一条进行连接
2)将新加入的点加入A集合
3)n-1次之后完成
代码:(摘自模版题洛谷3366的题解)
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define inf 0x7fffffff 4 #define maxn 5005 5 int cost[maxn][maxn],minn,n,m,v2[maxn],tot=1,now,ans; 6 bool v1[maxn]; 7 inline void getcost() 8 { 9 scanf("%d%d",&n,&m); 10 for(int i=1;i<=n;i++) 11 { 12 for(int j=1;j<=n;j++) 13 { 14 cost[i][j]=inf; 15 } 16 } 17 //先将数组赋为极大值 18 for(int i=1,u,v,w;i<=m;i++) 19 { 20 scanf("%d%d%d",&u,&v,&w); 21 if(cost[u][v]>w) 22 { 23 cost[u][v]=cost[v][u]=w; 24 } 25 } 26 //初始化cost数组 27 for(int i=1;i<=n;i++) 28 { 29 v2[i]=cost[1][i]; 30 } 31 v1[1]=1; 32 //找出与1节点相连的边并进行标记 33 } 34 inline int prim() 35 { 36 while(tot<n) 37 //最小生成树的概念,边数比点数小一 38 { 39 minn=inf; 40 //附上初值 41 tot++; 42 //已经使用边数++ 43 for(int i=1;i<=n;i++) 44 { 45 if(!v1[i]&&v2[i]<minn) 46 { 47 minn=v2[i]; 48 now=i; 49 } 50 } 51 //找出与该点相连的最小边 52 ans+=minn; 53 //更新答案 54 for(int i=1;i<=n;i++) 55 { 56 if(v2[i]>cost[now][i]&&!v1[i]) 57 { 58 v2[i]=cost[now][i]; 59 } 60 } 61 v1[now]=1; 62 //在找出与now节点相连的边并进行标记 63 } 64 return ans; 65 } 66 int main() 67 { 68 getcost(); 69 printf("%d",prim()); 70 return 0; 71 }
再次以图一为例,
输入:
4 5 1 2 40 1 3 20 1 4 30 2 3 30 4 3 10
输出:
60
完全正确。