最小生成树 ----克鲁斯卡尔(Kruskal)算法

之前知道有解决最小生成树的primkruskal两个算法

貌似两个算法好像针对的题很不一样,系统的解决一下这两个算法

注意:prim算法适合稠密图,其时间复杂度为O(n^2),其时间复杂度与边得数目无关,而kruskal算法的时间复杂度为O(eloge)跟边的数目有关,适合稀疏图。

 当图的边数为e时,Kruskal算法所需的时间是O(eloge)。当e = Ω(n^2)时,Kruskal算法比Prim算法差;但当e = o(n^2)时,Kruskal算法比Prim算法好得多。

kruskal----归并边;prim----归并点

并查集详解:https://blog.csdn.net/dellaserss/article/details/7724401

1.kruskal (+并查集)

mark:https://www.cnblogs.com/yoke/p/6697013.html

裸模板题:hdu 1232

#include<bits/stdc++.h>
using namespace std;
#define maxn 1000+10
//Accepted
//Time :31ms
//Memory :1384kB
//Length :668
//Lang :G++ 
int father[maxn];
void init(int n)//初始化 
{
	for(int i=1;i<=n;i++)
	{
		father[i]=i;
	}
}
int find(int x)//查 
{
	return x==father[x]?x:father[x]=find(father[x]);
 } 
 
void combine(int a,int b)//并 
{
	int fa=find(a);
	int fb=find(b);
	if(fa!=fb)
	{
		father[fa]=fb;
	}
 } 
int main()
{
	int n,m,u,v;
	long long ans=0;
	while(scanf("%d",&n)&&n)
	{
		init(n);
		ans=0;
		scanf("%d",&m);
		for(int i=1;i<=m;i++)
		{
			scanf("%d%d",&u,&v);
			combine(u,v);//连上 
		}
		for(int i=1;i<=n;i++) 
		{
			if(father[i]==i)
			ans++;//数一共有几块独立的连通区域 
		}
		printf("%lld\n",ans-1);//ans-1为把独立块联通的线的条数 
	}
	return 0;
}

hdu 4314

切除k个连着的点之间的线 即转化为:k个点的最小生成树

两种情况

1.两边的点都是危险点 这时候要删除 即转化为要计算权值

2.一边是危险点一边不是,要合并两个点的父亲节点 危险点的靠外 即 其父亲节点为新的树的父亲节点

//这样下次再遇到危险点就可删除啦!似乎get思想

#include<bits/stdc++.h>
#define maxn 100000+10
using namespace std; 
int flag[maxn];
int fa[maxn];
int t,k,n; 
void unit(int n)//初始化 
{
	for(int i=0;i<n;i++)
	    fa[i]=i;
}

int find(int x)
{
	return  x==fa[x]?x:fa[x]=find(fa[x]);
}

void combine(int a,int b)
{
	int s1=find(a);
	int s2=find(b);
	if(s1!=s2)
	fa[s1]=s2;
}

struct edge
{
	int u,v;
	int w;
	
}e[maxn];

bool cmp(edge a,edge b)
{
	return a.w>b.w;//从小到大 
 } 
 
void kruskal()
{
	long long ans=0;
	sort(e,e+n-1,cmp);
	
	//初始化
	unit(n);
	//最小生成顺
	for(int i=0;i<n-1;i++)
	{
		if(flag[find(e[i].u)]&&flag[find(e[i].v)])//两端是危险点 删除 //这棵树两端 
		{
			if(find(e[i].u)!=find(e[i].v))//两个顶点不在同一个集合里边
	    	{ 
			   //combine(e[i].u,e[i].v);
			   ans+=e[i].w; 
		    } 
		 }   
		else
		{
			if(flag[find(e[i].u)])//一端是危险点 
				{
				   combine(find(e[i].v),find(e[i].u));
				}
			else
				{
					combine(find(e[i].u),find(e[i].v));
				}
		}	
    } 
    cout<<ans<<endl;
}


int main()
{
	
	scanf("%d",&t);
	while(t--)
	{
		memset(flag,0,sizeof(flag));
		scanf("%d%d",&n,&k);
		for(int i=0;i<n-1;i++)
		{
			scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
		}
		
		for(int i=0;i<k;i++)
		{
			int a;
			scanf("%d",&a);
			flag[a]=1;	 
		}
		kruskal(); 
	}
	
} 

poj 1251

模板题吧 没啥可解释的

就是输入那块要规避空格啥的 所以用cin 吧 然后突然提醒了我要总结一下各种输入的特点 回头mark一下别人的博客

#include<iostream>
#include<cstdio> 
#include<algorithm>
#define maxn 1000
using namespace std;
int father[30]; 

struct ed
{
	int u,v,w;
}point[maxn];//开小了,re了一发55555555 一定要先算算哇 

bool cmp(ed a,ed b)
{
	return a.w<b.w;
}

void init(int n)
{
	for(int i=0;i<n;i++)//i=0或者1要看点哇
	{
		father[i]=i; 
	} 
}

int find(int x)
{
	return x==father[x]?x:father[x]=find(father[x]);
}

int combine(int a,int b)
{
	int fa=find(a);
	int fb=find(b);
	if(fa!=fb)
	{
		father[fa]=fb;//!!!!!!!!!!!!!!!!!!注意不是father【a】=fb 
	}
}
int main()
{
	char ch,sh;
	int n;
	while(scanf("%d",&n)&&n)
	{
		init(n);
		getchar();
		int q=0,time,wl,count=0;
		while(q<n-1)
		{
			
			cin>>ch>>time;
			//int us= 
			for(int i=count;i<count+time;i++)
			{
				cin>>sh>>wl;
				getchar();
				point[i].u=ch-'A';
				point[i].v=sh-'A';
				point[i].w=wl;
			}
			q++;
			count+=time;
		}
		//kruskal
		int ans=0;
		sort(point,point+count,cmp);
		for(int i=0;i<count;i++)
		{
			if(find(point[i].u)!=find(point[i].v))
			{
				combine(point[i].u,point[i].v);
				ans+=point[i].w;
			}
		}
		printf("%d\n",ans);
	}
	
} 

poj 1287

//Accepted
//16ms	0.7mb	

#include<iostream>
#include<algorithm> 
#include<cstdio>
using namespace std;
#define maxn 10000
int father[60];
struct node
{
	int u,v,w;
}ed[maxn];
bool cmp(node a,node b)
{
	return a.w<b.w;
}

void init(int n)
{
	for(int i=1;i<=n;i++)
	{
		father[i]=i;
	}
}
 int find(int x)
 {
 	return x==father[x]?x:father[x]=find(father[x]);
 }
 
 void combine(int a,int b)
 {
 	int fa=find(a);
 	int fb=find(b);
 	if(fa!=fb)
 	{
 		father[fa]=fb; 
	 }
 }
 
 int main()
 {
 	int n,m;
 	while(cin>>n&&n)
 	{
 		init(n);
 		cin>>m;
 		for(int i=1;i<=m;i++)
 		{
 			scanf("%d %d %d",&ed[i].u,&ed[i].v,&ed[i].w);
		 }
		 //
		 sort(ed+1,ed+m+1,cmp);
		 int ans=0;
		 for(int i=1;i<=m;i++)
		 {
		 	if(find(ed[i].u)!=find(ed[i].v))
		 	{
		 		ans+=ed[i].w;
				 combine(ed[i].u,ed[i].v); 
			 } 
		 }
		 cout<<ans<<endl;
	 }
 }

poj 2031

注意数学公式啊喂,哭了QQQQ

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
//Accepted
//32ms
//0.4mb
int father[1000];
using namespace std;
struct node
{
	int p;
	double x,y,z,r;
}ed[1000];

struct node2
{
	int u,v;
	double w;
}ed2[1000000];
bool cmp(node2 a,node2 b)
{
	return a.w<b.w;
}

void init(int n)
{
	for(int i=1;i<=n;i++)
	{
		father[i]=i;
	}
}

int find(int x)
{
	return x==father[x]?x:father[x]=find(father[x]);
}

void combine(int a,int b)
{
	int fa=find(a);
	int fb=find(b);
	if(fa!=fb)
	{
		father[fa]=find(fb);
	}
}
double dis(node a,node b)
{
	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z));
}

double cul(node a,node b)
{
	if(dis(a,b)<=a.r+b.r)
	{
	  	return 0;
	}
	else
	{
		return dis(a,b)-a.r-b.r;
	}
}
int main()
{
	int n;
	while(cin>>n&&n)
	{
	   init(n);
	   for(int i=1;i<=n;i++)
		{
			ed[i].p=i;
			scanf("%lf %lf %lf %lf",&ed[i].x,&ed[i].y,&ed[i].z,&ed[i].r);
		}
		int t=1;
		for(int i=1;i<n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				if(i==j)continue;
				else
				{
					ed2[t].u=ed[i].p;
				    ed2[t].v=ed[j].p;
				    ed2[t].w=cul(ed[i],ed[j]);
				    t++;
				}
				
			}
		}	
		t--;
		double ans=0;
		sort(ed2+1,ed2+t+1,cmp);
		//for(int i=1;i<=t;i++)
		//{
		//	printf("%.3f ",ed2[i].w);
		//}
		for(int i=1;i<=t;i++)
		{
			if(find(ed2[i].u)!=find(ed2[i].v))
			{
				combine(ed2[i].u,ed2[i].v);
				ans+=ed2[i].w;
			}
		}
	 printf("%.3f\n",ans);
	}
	return 0;
 } 

poj  2421

//Accepted
//Time 47ms
//Memory 240kB
#include<iostream>
#include<cstdio>
#include<algorithm>
#define maxn 10000+10
using namespace std;
int father[110];
int dis[110][110];
struct node
{
	int u,v,w;	
}ed[maxn];

void init(int n)
{
	for(int i=1;i<=n;i++)
	{
		father[i]=i;
	}
}

int find(int x)
{
	return x==father[x]?x:father[x]=find(father[x]); 
}

bool cmp(node a,node b)
{
	return a.w<b.w;
}

void combine(int a,int b)
{
	int fa=find(a);
	int fb=find(b);
	if(fa!=fb)
	{
		father[fa]=find(fb);
	 } 
}

int main()
{
	int n,q;
	cin>>n;
	init(n);
	int t=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			int p;
			scanf("%d",&p);
			if(j>i)
			{
				ed[++t].u=i;
				ed[t].v=j;
				ed[t].w=p;
				
			}
		}
	}
	//
	long long ans=0;
	sort(ed+1,ed+t+1,cmp);
	cin>>q;
	while(q--)
	{
		int a,b;
		scanf("%d %d",&a,&b);
		combine(a,b);
	}
	for(int i=1;i<=t;i++)
	{
		if(find(ed[i].u)!=find(ed[i].v))
		{
			combine(ed[i].u,ed[i].v);
			ans+=ed[i].w;
		}
	}
	printf("%lld",ans);
}

zoj 1586 

改变一下,把路径长度加上两端的权值

#include<iostream>
#include<cstdio>
#include<algorithm>
#define maxn 1000000+10
using namespace std;
int father[1010];
int prd[1010];
struct node
{
	int u,v,w;	
}ed[maxn];

void init(int n)
{
	for(int i=1;i<=n;i++)
	{
		father[i]=i;
	}
}

int find(int x)
{
	return x==father[x]?x:father[x]=find(father[x]); 
}

bool cmp(node a,node b)
{
	return a.w<b.w;
}

void combine(int a,int b)
{
	int fa=find(a);
	int fb=find(b);
	if(fa!=fb)
	{
		father[fa]=find(fb);
	 } 
}

int main()
{
	int n,t;
	cin>>t;
	while(t--)
	{
		cin>>n;
	init(n);
	int t=0;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&prd[i]);
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			int p;
			scanf("%d",&p);
			if(j>i)
			{
				ed[++t].u=i;
				ed[t].v=j;
				ed[t].w=p+prd[ed[t].u]+prd[ed[t].v];
				
			}
		}
	}
	//
	long long ans=0;
	sort(ed+1,ed+t+1,cmp);
	for(int i=1;i<=t;i++)
	{
		if(find(ed[i].u)!=find(ed[i].v))
		{
			combine(ed[i].u,ed[i].v);
			ans+=ed[i].w;
		}
	}
	printf("%lld\n",ans);
	}
	
}
 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值