P3366 【模板】最小生成树 题解
Description
给出一个无向网,求该无向网的最小生成树。各条边的权重不超过100000。
Input
输入的第一行是一个整数N,表示该网的顶点个数。 3 ≤ N ≤ 100
接下来是N行,每行N个整数,表示每个顶点到其余顶点的距离。
Output
输出该最小生成树的权重。
Sample Input
4
0 4 9 21
4 0 8 17
9 8 0 16
21 17 16 0
Sample Output
28
先从小到大排序,再用并查集判断起始点是否联通。
先贴代码,具体内容待之后有空补充
#include
using namespace std;
int f[110],n;
struct node
{
int u,v,dis;
}Node[11000];
int cmp(node s1,node s2)
{
return s1.dis<s2.dis;
}
/*以下三个函数均为并查集的操作*/
void init()///f数组初始化
{
for(int i=1;i<=n;i++){
f[i]=i;
}
}
int find_(int x)///找祖先
{
if(f[x]==x)
return x;
else
return f[x]=find_(f[x]);
}
int merge(int u,int v)///判断u和v当前是否在同一集合内(即是否联通),如果不是则进行合并操作
{
int x,y;
x=find_(u);
y=find_(v);
if(x!=y){
f[y]=x;
return 1;
}
return 0;
}
int main()
{
ios::sync_with_stdio(false);
int m=0,sum=0,x;
cin>>n;
init();
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>x;
if(i!=j)
Node[m].u=i,Node[m].v=j,Node[m++].dis=x;
}
}
sort(Node,Node+m,cmp);
int k=0;
for(int i=0;i<m;i++){
if(merge(Node[i].u,Node[i].v)){
sum+=Node[i].dis;
k++;
}
if(k==n-1)
break;
}
cout<<sum<<endl;
return 0;
}
没有任何优化的做法:
#include
using namespace std;
const int inf=0x3f3f3f3f;
int n,G[110][110],dis[110],ans;
bool book[110];
void prim()
{
for(int i=1;i<=n;i++){///以顶点1为跟点,dis数组中存储顶点1到其余顶点的距离
dis[i]=G[1][i];
}
book[1]=1,dis[1]=0;///book数组标记顶点1已用过,
int Min,k;
for(int i=1;i<=n-1;i++){
k=0,Min=inf;///初始化Min为无穷大
for(int j=1;j<=n;j++){
if(!book[j]&&dis[j]<Min){ ///寻找i到j的距离最小值
Min=dis[j];
k=j;
}
}
ans+=dis[k];
///f(k==0)return;这句有没有都对,思考下这一句究竟有没有存在的作用
book[k]=1;
for(int u=1;u<=n;u++){
if(!book[u]&&dis[u]>G[k][u])
dis[u]=G[k][u]; ///维护dis数组
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>G[i][j];
}
}
prim();
cout<<ans<<endl;
}
(special judge)Time Limit: 4000/2000 MS (Java/Others)
Description
给出一个无向图,求该图的最小生成树。
Input
多测试用例。
第一行:两个正整数 N 和 E ( 0 < N < 10000, N < E < 40000 ),分别表示该图的顶点个数、边的总数。顶点编号从 0~N-1
接下来E行,每行是3个整数:u v w,表示 顶点u 与 顶点v 之间有一条权值为w的边。 0 ≤ u ,v < N , 0 < w < 20
Output
每个测试用例:
如果该图不连通,输出一行: unconnected graph
否则输出n行:第1行是该生成树的边权之和,第2~n行以 u v w 的形式输出生成树的各条边,其中u和v表示这条边的两个顶点,w表示这条边的权重。生成树的边的输出次序不限,只要求不重复、不遗漏。 如:3 7 2 跟 7 3 2 是同一条边。
Sample Input
9 14
0 2 4
0 1 8
2 1 11
2 3 8
1 4 7
1 5 1
4 3 2
5 4 6
5 7 2
7 3 4
6 3 7
7 6 14
6 8 9
7 8 10
Sample Output
37
2 0 4
5 1 1
3 2 8
3 4 2
5 7 2
3 7 4
3 6 7
8 6 9
耗时:1305MS
#include
#include
using namespace std;
struct node
{
int u,v,edge;
}G[80008],T[10005];
int f[10005];
int cmp(node s1,node s2)
{
return s1.edge<s2.edge;
}
void init(int n)
{
for(int i=0;i<=n;i++){
f[i]=i;
}
}
int get(int x)
{
if(f[x]==x)
return x;
return get(f[x]);
}
int merge(int u,int v)
{
int t1,t2;
t1=get(u);
t2=get(v);
if(t1!=t2){
f[t2]=t1;
return 1;
}
return 0;
}
int main()
{
ios::sync_with_stdio(false);
int N,E;
while(!(cin>>N>>E).eof()){
for(int i=1;i<=2*E;i+=2){
cin>>G[i].u>>G[i].v>>G[i].edge;
G[i+1].u=G[i].v,G[i+1].v=G[i].u,G[i+1].edge=G[i].edge;///无向图
}
init(N);///f数组初始化
sort(G+1,G+2*E+1,cmp);///按边从小到大排
int dis=0,k=0;
for(int i=1;i<=2*E;i++){
if(merge(G[i].u,G[i].v)){///u,v两点联通
dis+=G[i].edge;
T[k].u=G[i].u,T[k].v=G[i].v,T[k++].edge=G[i].edge;
}
if(k==N-1)break;
}
if(k<N-1)///该图不连通
cout<<"unconnected graph"<<endl;
else{
cout<<dis<<endl;
for(int j=0;j<k;j++){
cout<<T[j].u<<' '<<T[j].v<<' '<<T[j].edge<<endl;
}
}
}
return 0;
}
从策略上来说,Prim算法是直接查找,多次寻找邻边的权重最小值,而Kruskal是需要先对权重排序后查找的~
所以说,Kruskal在算法效率上是比Prim快的,因为Kruskal只需一次对权重的排序就能找到最小生成树,而Prim算法需要多次对邻边排序才能找到~
prim:该算法的时间复杂度为O(n2)。与图中边数无关,该算法适合于稠密图。
kruskal:需要对图的边进行访问,所以克鲁斯卡尔算法的时间复杂度只和边又关系,可以证明其时间复杂度为O(eloge)。适合稀疏图