原题链接
题目大意
给出一张 n n n 个点的联通图,每条边都有一个权值,求这个图的最小生成树。
结题思路
普里姆算法(prim)
这是一个时间复杂度为
O
(
n
2
)
O(n^2)
O(n2) 的做法,从根本来说,是一个使用贪心算法实现的最小生成树算法。
假设这样的一张图:
从1出发
进行扩展,则可以得到一张从A到各个位置点距离表格
1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|
0 | 2 | 4 | 7 | ∞ \infty ∞ |
按照贪心思路,我们选择最短的边,并且跟新表格:
1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|
0 | 2 | 1 | 7 | 2 |
至三的距离最小,于是便继续扩展,更新表格:
1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|
0 | 2 | 1 | 1 | 2 |
……
最终,我们可以得到:
1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|
0 | 2 | 1 | 1 | 2 |
代码实现
#include<iostream>
#include<fstream>
#include<set>
#include<map>
#include<ctime>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
int n,p[110][110],Min[110];
bool v[110];
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>p[i][j];
memset(Min,0x7f,sizeof(Min));//赋最大值
memset(v,true,sizeof(v));//每一个位置都没有去过
Min[1]=0;//标记第一个点
int next;
for(int i=1;i<=n;i++){
next=0;
for(int j=1;j<=n;j++){
if(Min[j]<Min[next]&&v[j])//向外扩展,去没有去过的点
next=j;
}
v[next]=false;//标记
for(int j=1;j<=n;j++){
if(v[j]&&Min[j]>p[next][j])//记录目前到每一个点的最短距离
Min[j]=p[next][j];
}
}
int ans=0;
for(int i=1;i<=n;i++)
ans+=Min[i];
cout<<ans;
return 0;
}
克鲁斯卡尔(kruskal)
这是一个时间复杂度为
O
(
n
∗
l
o
g
2
n
)
O(n*log_2n)
O(n∗log2n) 的算法,使用了并查集算法。
假设这样的一张图:
将每一条边进行排序,从小到大选择,直到选完了 n − 1 n-1 n−1 条边:
最后将所有被选中点边的边权相加,便是最终答案。
代码实现
#include<iostream>
#include<fstream>
#include<set>
#include<map>
#include<ctime>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
struct node{
int num,f,t;
};
vector<node> Map;
int f[110],n,t;
bool cmp(node q,node h)//排序条件
{
return q.num<h.num;
}
int fin(int n)//查找祖先
{
if(n!=f[n])
f[n]=fin(f[n]);
return f[n];
}
void un(int x,int y)//合并集合
{
f[fin(x)]=fin(y);
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
cin>>t;
Map.push_back((node){t,i,j});//记录边,无向图
Map.push_back((node){t,j,i});//记录边,无向图
}
sort(Map.begin(),Map.end(),cmp);//排序
int k=0,ans=0;//记录当前选择的个数和当前的答案
for(int i=1;i<=n;i++)//并查集初始化
f[i]=i;
for(int i=0;i<Map.size();i++){//合并
if(fin(Map[i].f)!=fin(Map[i].t)){//两点未合并
k++;
ans+=Map[i].num;
un(Map[i].f,Map[i].t);
}
if(k==n-1){//已经形成了一棵树
cout<<ans;//结束程序
return 0;
}
}
return 0;
}
样例
输入
3
0 1 2
1 0 1
2 1 0
输出
2