题目大意
例题链接
普通的最小生成树,给出
n
n
n个农场,求最短需要多长的光纤才能连接所有的农场。
解题思路
既然是最普通的最小生成树,那么就需要了解两种算法。
prim算法
prim算法采用的是蓝白点思想,把已进入最小生成树的点标为白点,未进入的标为蓝点,具体如下的例子。
如图,初始时所有点都未加入最小生成树,
m
i
n
1
=
0
,
m
i
n
[
2
,
3
,
4
,
5
]
=
∞
。
m
i
n
min_1=0,min[2,3,4,5]=∞。min
min1=0,min[2,3,4,5]=∞。min表示每个店离白点的最短距离,设初始点1为白点。
第一次循环找到离白点距离最小的蓝点1,将它加入最小生成树,
a
n
s
+
=
m
i
n
1
ans+=min_1
ans+=min1。
接下来更新与点1相连的点与白点的最小距离,
m
i
n
[
2
,
3
,
4
,
5
]
=
[
2
,
4
,
7
,
∞
]
min[2,3,4,5]=[2,4,7,∞]
min[2,3,4,5]=[2,4,7,∞]。
接着再找到离白点最近的蓝点,也就是点2,将它加入最小生成树。
a
n
s
+
=
m
i
n
2
ans+=min_2
ans+=min2
再次更新所有点与白点的最小距离,
m
i
n
[
3
,
4
,
5
]
=
[
1
,
7
,
2
]
min[3,4,5]=[1,7,2]
min[3,4,5]=[1,7,2]。
再找到与当前点2相邻的最近的蓝点,也就是点3,将它加入最小生成树
a
n
s
+
=
m
i
n
3
ans+=min_3
ans+=min3
以此类推,使最后所有点都为白点,得到MST=6。一共进行
n
n
n次循环,每次循环都将一个点加入最小生成树,一共添加
n
−
1
n-1
n−1条边,最后
n
n
n个点全部加入最小生成树,时间复杂度为
O
(
n
2
)
O(n^2)
O(n2)。
kruskal算法
kruskal采用的是并查集,每次枚举距离最小的两个点,如果这两个点不存在于一个集合,就表示这两个点未与最小生成树连接,那么就把它们连接起来,当连接了
n
−
1
n-1
n−1条边时,则所有的点都已被连接。
需要初始化将每两个点的距离先排序一遍。
给出一个这样的图。
首先每个店都不是连通的。
先找到距离最短的两个点,也就是点1和点3,距离是1。将它们连接起来。
其次再找到点4和点6,距离为2,连接
以此类推,直到将所有的点都连接,也就是连接了
n
−
1
n-1
n−1条边后,我们就求出了最小生成树。
因为最后遍历时只需要
O
(
E
)
O(E)
O(E)的时间复杂度(
E
E
E为边数),因此kruskal的时间复杂度为排序所花费的
O
(
E
∗
l
o
g
2
E
)
O(E*log_2E)
O(E∗log2E)。
代码
prim算法
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,g[105][105],minn[105];
bool v[105];
int main()
{
cin>>n;
memset(minn,0x7f,sizeof(minn));
minn[1]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>g[i][j];
for(int i=1;i<=n;i++)
{
int p=0;
for(int j=1;j<=n;j++)
if(!v[j]&&minn[j]<minn[p])
p=j;
v[p]=1;
for(int j=1;j<=n;j++)
if(!v[j]&&g[p][j]<minn[j])
minn[j]=g[p][j];
}
int ans=0;
for(int i=1;i<=n;i++)
ans+=minn[i];
cout<<ans;
return 0;
}
kruskal算法
#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n,tot,minn[105],p[105],head[105];
bool v[105];
struct ap{
int x,y,w;
}g[10005];
int find(int x)
{
if(p[x]==x) return x;
return p[x]=find(p[x]);
}
void join(int x,int y)
{
p[find(x)]=p[find(y)];
}
void app(int x,int y,int w)
{
g[++tot].x=x;
g[tot].y=y;
g[tot].w=w;
}
bool cmp(ap x,ap y)
{
return x.w<y.w;
}
int main()
{
cin>>n;
memset(minn,0x7f,sizeof(minn));
minn[1]=0;
for(int i=1;i<=n;i++) p[i]=i;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
int x;
cin>>x;
if(x) app(i,j,x);
}
int ans=0,k=0;
sort(g+1,g+tot+1,cmp);
for(int i=1;i<=tot;i++)
{
if(find(g[i].x)!=find(g[i].y))
{
join(g[i].x,g[i].y);
ans+=g[i].w;
k++;
}
if(k==n-1) break;
}
cout<<ans;
return 0;
}