最小生成树(Kruskal和prim以及prim与dijkstra的对比)

板子板子板子板子。。。。。。。。。。。。。
1. k r u s k a l kruskal kruskal算法

//Author:melody
#include<bits/stdc++.h>
using namespace std;

const int mm=200010;
const int nn=5010;

int n,m;
int fa[nn];
int ans=0; 

struct edge
{
	int x,y,z;
}e[mm*2];

inline int read()
{
	int x=0,f=1; char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-') f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
	return x*f;
}

bool mycmp(edge x,edge y)
{
	return x.z<y.z;
}

int gf(int x)
{
	if(fa[x]==x)	return x;
	return fa[x]=gf(fa[x]);
}

int main()
{
	n=read(); m=read();
	for(int i=1;i<=n;++i)	fa[i]=i;
	for(int i=1;i<=m;++i)
	{
		e[i].x=read();
		e[i].y=read();
		int xx=gf(e[i].x);
		int yy=gf(e[i].y);
		if(xx!=yy)	fa[xx]=yy;
		e[i].z=read();
	}
	int cnt=0;
	for(int i=1;i<=n;++i)
		if(gf(i)==i)	cnt++; 
	if(cnt>=2)	
	{
		cout<<"orz"<<endl;
		return 0; 
	}
	//并查集初始化
	for(int i=1;i<=n;++i)	fa[i]=i;
	//按照边权进行排序
	sort(e+1,e+1+m,mycmp);/*注意在这里排序的是边数m,不要打错成n*/
	//求最小生成树
	for(int i=1;i<=m;++i)
	{
		int xx=gf(e[i].x);
		int yy=gf(e[i].y);
		if(xx==yy)	continue;	
		fa[xx]=yy;
		ans+=e[i].z;
	}
	cout<<ans<<endl;
	return 0;
}

2. P r i m Prim Prim算法

//melody
//Prim
void prim()
{
	memset(d,0x3f3f3f,sizeof(d));/*记录被加入集合T时的边权*/
	memset(v,0,sizeof(v));/*标记该点是否已经加入了集合T*/
	d[1]=0;/*以代价为0的状态加入到集合T*/
	for(int i=1;i<=n;++i)
	{
		int x=0;
		for(int j=1;j<=n;++j)
			if(!v[j]&&(x==0||d[j]<d[x]))	x=j;/*找到集合s中边权最小的点,这一点就是和dijkstra的相似之处,找到全局的最优解*/ 
		v[x]=1;
		for(int y=1;y<=n;++y)
			if(!v[y])	d[y]=min(d[y],a[x][y]);/*用全局最优解更新其他的点*/
	}
}

int main()
{
	cin>>n>>m;
	//构建邻接矩阵
	memset(a,0x3f,sizeof(a));
	for(int i=1;i<=n;++i)	a[i][i]=0;
	for(int i=1;i<=m;++i)
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		a[y][x]=a[x][y]=min(a[x][y],z);
	}
	//求最小生成树
	prim(); 
	for(int i=2;i<=n;++i)	ans+=d[i];/*prim的答案是从2开始统计的*/
	cout<<ans<<endl;
	return 0; 
}

我觉得 k r u s k a l kruskal kruskal算法和 P r i m Prim Prim算法不同的是建立基础的思想不一样。

  1. K r u s k a l Kruskal Kruskal算法是基于边的思想,不断找到边权最小的边,判断是不是可以连接两个不同的连通块,努力去构建你心目中的最小生成树;
  2. P r i m Prim Prim更侧重的就是了,目标状态是所有的点都在最小生成树中,建立两个集合T,S;T表示的是已经建成的最小生成树中的点,S表示的是未用过的点,那么不断找到S中的全局最优解加入到集合T中,之后用这个点去更新其他的未被用过的点;重复以上的操作,直到S为空。更像是求最短路中的 D i j k s t r a Dijkstra Dijkstra算法

对比与 D i j k s t r a Dijkstra Dijkstra的相同与不同

//Dijkstra
#include<bits/stdc++.h>
using namespace std;

const int nn=1e6;
const int mm=1e5;

int n,m;
int d[nn],vis[nn]; 

struct edge
{
	int to,ver,nex;
}e[mm*2];
int num=0;
int last[nn];

void add(int x,int y,int z)
{
	e[++num].to=y; e[num].ver=z;
	e[num].nex=last[x]; last[x]=num;
}

priority_queue<pair<int,int> >q;

void dijkstra(int xx)/*起点*/
{
	memset(d,0x3f3f3f,sizeof(d));/*相似点1:都需要两个数组的初始化*/
	memset(vis,0,sizeof(vis));
	d[xx]=0;/*相似点2:都是根节点以0的状态进入到最优集合T*/
	q.push(make_pair(0,xx));
	while(q.size())
	{
		int x=q.top().second; q.pop();
		if(vis[x])	continue;/*避免最优集合中的点的重复遍历*/
		vis[x]=true;/*相似点3:被选入最优集合的都会有标记*/
		for(int i=last[x];i;i=e[i].nex)
		{
			int y=e[i].to;
			int z=e[i].ver;
			if(d[x]+z<d[y])/*就算被标记过,也可能再次被更新*/
			{/*相似点4:都是用最优集合点去更新其他的点*/
				d[y]=d[x]+z;
				q.push(make_pair(-d[y],y));
			}
		}
	}
}

int main()
{
	cin>>n>>m;
	for(int i=1;i<=m;++i)//建立邻接表 
	{
		int x,y,z;
		scanf("%d%d%d",&x,&y,&z);
		add(x,y,z); 
	 } 
	dijkstra(1);//求最短路 
	for(int i=1;i<=n;++i)
		printf("%d\n",d[i]);
	return 0;
}

相似点:

  1. 都需要两个数组的初始化;
  2. 都是根节点以0的状态进入到最优集合T;
  3. 被选入最优集合的都会有标记;
  4. 都是用最优集合点去更新其他的点。

不同的地方:

dijkstra更侧重于邻接表的存储,而prim更侧重于邻接矩阵的存储;

愿你有实力,有才华,主动出击,为自己争取不一样的人生。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值