【贪心算法】最小生成树

(带全无向连通)图的所有生成树中具有边上的权值之和最小的树称为图的最小生成树。 

1.Prim算法

主要思想:Prim是先选出一个源点,将它放入已选集合中,从与已选集合中的点相连,且另一点没有被选中的权值最小的边,将这个点也放入已选集合中,重复之前的步骤。每一次循环都会找到一条边,所以循环进行verNum-1次。

//6 10   1 2 6  1 3 1  1 4 5  2 3 5  2 5 3  3 4 5  3 5 6  3 6 4  4 6 2  5 6 6  
//9 14   1 2 4  1 8 8  2 3 8  2 8 11  3 4 7  3 6 4  3 9 2  4 5 9  4 6 14  5 6 10  6 7 2  7 8 1  7 9 6  8 9 7
#include<iostream>
using namespace std;
#define MAX_V 5005
#define MAX 0x7fffff
int verNum,edgeNum,sum=0;
int edge[MAX_V][MAX_V];
int pre[5005];//数组用于临时存储当前选中边的另一个端点,每次输出选中情况后更新为-1不在参与接下来的工作
void Prim()
{
	int *dist=new int[verNum+1];//数组从1开始,防RE
	for(int i=1;i<=verNum;i++)
	{
		dist[i]=edge[1][i];
		pre[i]=1;
	}
	pre[1]=-1;//以1作为源点
	for(int i=1;i<verNum;i++)
	{
		int temp=MAX;
		int x=0;
		for(int j=1;j<=verNum;j++)
		{
			if(pre[j]!=-1&&dist[j]<temp)//寻找未选中的距离最小的边
			{
				x=j;
				temp=dist[j];
			}
		}
		//cout<<pre[x]<<"->"<<x<<":"<<temp<<endl;
		pre[x]=-1;
		sum+=temp;
		for(int i=2;i<=verNum;i++)
		{
			if(pre[i]!=-1&&edge[x][i]<dist[i])
			{
				dist[i]=edge[x][i];//区分于单源最短路径,dist数组表示的是该点到当前最小生成树的边的距离,不是到源点的距离
				pre[i]=x;
			}
		}
	}
}
int main()
{
	cin>>verNum>>edgeNum;
	for(int i=1;i<=verNum;i++)
		for(int j=1;j<=i;j++)
			edge[i][j]=edge[j][i]=MAX;
	for(int i=1;i<=edgeNum;i++)
	{
		int a,b,c;
		cin>>a>>b>>c;
		edge[b][a]=edge[a][b]=min(c,edge[a][b]);//洛谷防重边
	}
	Prim();
	bool flag=true;
	for(int i=1;i<=verNum;i++)
		if(pre[i]!=-1)
		{
			flag=false;
			break;
		}
	if(flag==true)
		cout<<sum<<endl;
	else
		cout<<"orz"<<endl;
	return 0;
}

2.Kruskal算法

主要思想:Kruskal主要是利用了查并集的思想,先将边按从小到大排列,然后从权值小的开始,如果边的两个端点不在一个集合就可以输出,然后将它们合并到一个集合中。

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
//6 10   1 2 6  1 3 1  1 4 5  2 3 5  2 5 3  3 4 5  3 5 6  3 6 4  4 6 2  5 6 6  
//9 14   1 2 4  1 8 8  2 3 8  2 8 11  3 4 7  3 6 4  3 9 2  4 5 9  4 6 14  5 6 10  6 7 2  7 8 1  7 9 6  8 9 7
int vertexnum,edgenum;//点的总数和边的总数
int root[5005],ans=0;//记录每个点的上级
struct Graphic
{
	int weight;//权重
	int x;
	int y;
}g[200005];
bool cmp(Graphic a,Graphic b)
{
	return a.weight<b.weight;
}

int find(int x)//查找x的根
{
	if(root[x]==x)
		return x;
	return root[x]=find(root[x]);
}
int merge(int x,int y)
{
	int rx=find(x),ry=find(y);
	if(rx==ry)
		return 0;
	root[rx]=ry;
	return 1;
}
void kruskal()
{
	int i,j;
	for(int k=1;k<=edgenum;k++)
	{
		i=g[k].x;
		j=g[k].y;
		if(merge(i,j))//如果没有在一个集合内
			ans+=g[k].weight;
			cout<<i<<" -> "<<j<<" : "<<g[k].weight<<endl;
	}
}
int main()
{
	cin>>vertexnum>>edgenum;
	for(int i=1;i<=vertexnum;i++)
		root[i]=i;
	for(int i=1;i<=edgenum;i++)
		cin>>g[i].x>>g[i].y>>g[i].weight;
	sort(g,g+edgenum+1,cmp);//按权重从小到大排序
	kruskal();
	int i;
	for(i=1;i<vertexnum;i++)
		if(root[i]!=root[i+1])
		{
			cout<<"orz"<<endl;
			break;
		}
	if(i==vertexnum)
		cout<<ans<<endl;
	return 0;
}

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值