19 最小生成树(MST)
理论说明
本节我们了解图论中的一类经典问题---最小生成树。
在一个无向连通图中,如果存在一个连通子图包含原图中所有的结点和部分边,且这个子图中不存在回路,那么我们称这个子图为原图的一棵生成树。在带权图中,所有的生成树中边权的和最小的数(可能不唯一),被称为最小生成树。
最小生成树问题是图论的经典问题之一,它在实际生活中也有广泛的应用,如在通信基站之间修建通信光缆使所有的基站之间可以直接或者间接通信,最少需要多少长的光缆。要利用最小生成树来解决实际问题,我们必须先学会怎么求解连通图的最小生成树。
这里介绍Kruskal算法的基本步骤:
-
初始时所有结点属于孤立的结点
-
按照边权顺序遍历所有的边,若遍历到的边两个顶点分属不同的集合,则确定该边为最小生成树的一条边,并将这两个顶点分属的集合合并。
-
遍历完所有边后,原图上所有结点属于同一个集合则选取的边和抟土中所有结点构成最小数;否则原图不连通,最小生成树不存在。
题目来源和说明
2006年浙江大学计算机及软件工程研究生机试真题
题目描述
某省调查乡村交通状况,得到的统计表中列出了任意两村庄间的距离。省政府“畅通工程”的目标是使全省任何两个村庄间都可以实现公路交通(但不一定有直接的公路相连,只要能间接通过公路可达即可),并要求铺设的公路总长度为最小。请计算最小的公路总长度。
输入说明
测试输入包含若干测试用例。每个测试用例的第1行给出村庄数目N ( < 100 );随后的N(N-1)/2行对应村庄间的距离,每行给出一对正整数,分别是两个村庄的编号,以及此两村庄间的距离。为简单起见,村庄从1到N编号。 当N为0时,输入结束,该用例不被处理。
输出说明
对每个测试用例,在1行里输出最小的公路总长度。
样例展示
输入:
3
1 2 1
1 3 2
2 3 4
4
1 2 1
1 3 4
1 4 1
2 3 3
2 4 2
3 4 5
0
输出:
3
5
C++代码
#include<iostream>
#include<algorithm>
using namespace std;
const int N=105;
int T[N];
int find(int x) {
if(T[x]==-1) return x;
else {
int temp=find(T[x]);
T[x]=temp;
return temp;
}
}
struct Edge{
int a,b;
int cost;
int build;
bool operator<(const Edge &A) const {
return cost<A.cost;
}
}edge[600000];
int main() {
int n;
while(scanf("%d",&n)!=EOF&&n!=0) {
for(int i=1;i<=n;i++) T[i]=-1;
for(int i=1;i<=n*(n-1)/2;i++) {
scanf("%d%d%d",&edge[i].a,&edge[i].b,&edge[i].cost);
}
sort(edge+1,edge+1+n*(n-1)/2);
int ans=0;
for(int i=1;i<=n*(n-1)/2;i++) {
int a=find(edge[i].a);
int b=find(edge[i].b);
if(a!=b) {
T[a]=b;
ans+=edge[i].cost;
}
}
printf("%d\n",ans);
}
return 0;
}
继续畅通工程
C++代码
//这个题目和之前差不多,只需要判断,如果建好了,cost改成0,就多了这一步
#include<iostream>
#include<algorithm>
using namespace std;
const int N=105;
int T[N];
int find(int x) {
if(T[x]==-1) return x;
else {
int temp=find(T[x]);
T[x]=temp;
return temp;
}
}
struct Edge{
int a,b;
int cost;
int build;
bool operator<(const Edge &A) const {
return cost<A.cost;
}
}edge[600000];
int main() {
int n;
while(scanf("%d",&n)!=EOF&&n!=0) {
for(int i=1;i<=n;i++) T[i]=-1;
for(int i=1;i<=n*(n-1)/2;i++) {
scanf("%d%d%d%d",&edge[i].a,&edge[i].b,&edge[i].cost,&edge[i].build);
if(edge[i].build==1) {
edge[i].cost=0;
}
}
sort(edge+1,edge+1+n*(n-1)/2);
int ans=0;
for(int i=1;i<=n*(n-1)/2;i++) {
int a=find(edge[i].a);
int b=find(edge[i].b);
if(a!=b) {
T[a]=b;
ans+=edge[i].cost;
}
}
printf("%d\n",ans);
}
return 0;
}
畅通工程
C++代码
//在最后加一个判断就行,如果有多个根节点说明不连通
#include<iostream>
#include<algorithm>
using namespace std;
const int N=105;
int T[N];
int find(int x) {
if(T[x]==-1) return x;
else {
int temp=find(T[x]);
T[x]=temp;
return temp;
}
}
struct Edge{
int a,b;
int cost;
bool operator<(const Edge &A) const {
return cost<A.cost;
}
}edge[600000];
int main() {
int n,m;
while(scanf("%d%d",&m,&n)!=EOF&&m!=0) {
for(int i=1;i<=n;i++) T[i]=-1;
for(int i=1;i<=m;i++) {
scanf("%d%d%d",&edge[i].a,&edge[i].b,&edge[i].cost);
}
sort(edge+1,edge+1+m);
int ans=0;
for(int i=1;i<=m;i++) {
int a=find(edge[i].a);
int b=find(edge[i].b);
if(a!=b) {
T[a]=b;
ans+=edge[i].cost;
}
}
int cnt=0;
for(int i=1;i<=n;i++) {
if(T[i]==-1) cnt++;
}
if(cnt==1) printf("%d\n",ans);
else printf("?\n");
}
return 0;
}
Freckles
https://www.nowcoder.com/questionTerminal/41b14b4cd0e5448fb071743e504063cf
C++代码
//这个问题和上面差不多,唯一的区别是这里的边长权重我要自己算一下就行啦
#include<iostream>
#include<math.h>
#include<algorithm>
using namespace std;
const int N=101;
int T[N];
int find(int x) {
if(T[x]==-1) return x;
else {
int temp=find(T[x]);
T[x]=temp;
return temp;
}
}
struct Edge{
int a,b;
double cost;
bool operator<(const Edge& A) const {
return cost<A.cost;
}
}edge[6000];
struct Point{
double x,y;
double getDistance(Point A) {
double tmp=(x-A.x)*(x-A.x)+(y-A.y)*(y-A.y);
return sqrt(tmp);
}
}list[101];
int main() {
int n;
while(scanf("%d",&n)!=EOF) {
for(int i=1;i<=n;i++)
T[i]=-1;
for(int i=1;i<=n;i++) { //读入每个点
scanf("%lf%lf",&list[i].x,&list[i].y);
}
int size=0; //求出所有的边
for(int i=1;i<=n;i++) {
for(int j=i+1;j<=n;j++) {
edge[size].a=i;
edge[size].b=j;
edge[size].cost=list[i].getDistance(list[j]);
size++;
}
}
sort(edge,edge+size);
double ans=0;
for(int i=0;i<size;i++) {
int a=find(edge[i].a);
int b=find(edge[i].b);
if(a!=b) {
T[a]=b;
ans+=edge[i].cost;
}
}
printf("%.2f\n",ans);
}
return 0;
}
Jungle Roads
C++代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=100;
int T[N];
int find(int x) {
if(T[x]==-1) return x;
else {
int temp=find(T[x]);
T[x]=temp;
return temp;
}
}
struct Edge{
int a,b;
int cost;
bool operator<(const Edge& A) const {
return cost<A.cost;
}
}e[100];
int main() {
int n;
char s;
while(scanf("%d",&n)!=EOF&&n!=0) {
int size=0;
int ncmp=n-1; //个数
while(ncmp--) {
cin>>s; //起点
int xnum; //边的个数
scanf("%d",&xnum); //边的个数
int a=s-'A'+1; //起点序号
while(xnum--) {
cin>>s; //终点
int x; //起点->终点的路径长度
scanf("%d",&x); //起点->终点的路径长度
int b=s-'A'+1; //终点序号
e[size].a=a;
e[size].b=b;
e[size].cost=x;
size++;
}
}
int ans=0;
sort(e,e+size);
for(int i=1;i<=n;i++) T[i]=-1;
for(int i=0;i<size;i++) {
int a=find(e[i].a);
int b=find(e[i].b);
if(a!=b) {
T[a]=b;
ans+=e[i].cost;
}
}
printf("%d\n",ans);
}
}
Leetcode题目太多,不知道如何准备高校夏令营?欢迎关注本专栏,和本小白一起准备夏令营吧! 本专题的规划如下: 截止到4月下旬:以王道考研为依托,提供夏令营机考的准备计划,打好基础 截止到5月中旬:以剑指offer进一步加强 本专题的内容如下: 1. 给出题目的在线测试链接,方面您检查代码的正确性 2. 给出题解