加载中…

返回上一页

最小生成树、拓扑排序

最小生成树

最小生成树,是用来从一个连通图中计算一个最小使所有点连通所需的权值.

因为要保证权值尽可能小,因此一定不会多连任何一条边. 所以最终选择出来的边连接起来一定是一棵树.

Prim

Prim算法采用与 Dijkstra、Bellman-Ford 算法一样的“蓝白点”思想:白点代表已经进入最小生成树的点,蓝点代表未进入最小生成树的点.

算法描述:(时间复杂度n2(邻接矩阵),邻接表 O(E log(n)) 其中 E 为边数)

1为起点生成最小生成树,min[v]表示蓝点v与白点相连的最小边权.

MST表示最小生成树的权值之和.

a)初始化:min[v]=∞(v≠1);min[1]=0;MST=0;

b)for(i=1;i<=n;i++)

  1. 寻找min[u]最小的蓝点u
  2. u标记为白点;
  3. MST+=min[u]
  4. for 与白点u相连的所有蓝点v
    if (w[u][v]<min[v])
    min[v]=w[u][v];

c)算法结束: MST即为最小生成树的权值之和

算法分析&思想讲解:

Prim算法每次循环都将一个蓝点u变为白点,并且此蓝点u与白点相连的最小边权min[u]还是当前所有蓝点中最小的. 这样相当于向生成树中添加了n-1次最小的边,最后得到的一定是最小生成树.

实现:

inline void prim(rll x)
{
	ans=0;//记录造价	
	for(rll i=1;i<=n;i++) dis[i]=mp[x][i];//把x放入已选集合,更新其他点到x的距离
	dis[x]=0;//x到自己的距离为0
	for(rll i=2;i<=n;i++)//只需要找到n-1条边就可以形成一棵最小生成树
	{
		rll mn=INT_MAX>>1,k;//min找最短的符合要求的边,k记录边的另一节点
		for(rll j=1;j<=n;j++)//枚举每一个点,找离已选点最近的边
			if(dis[j]<mn&&dis[j])//dis[j]=0表示j点已选
				mn=dis[j],k=j;
		ans+=dis[k]; dis[k]=0;//dis[k]设为0,表示k点一进入已选点
		for(rll j=1;j<=n;j++)//更新其他点到k点的距离
			if(dis[j]>mp[k][j]) dis[j]=mp[k][j];
	}
}

Kruskal

Kruskal 算法每一次会从剩余的边中选择一条花费最小且不会产生赘余的边,将其变为已选边.

它的主要步骤:先把边按照权值排序;然后加入边时判断加入后是否还是树;如果加入边的两个端点属于两棵不同的树时,将它们合并.

实现:

struct node
{
	ll x,y,v,id;
	inline friend bool operator<(rg node a,rg node b) { return a.v<b.v; }
}val[maxn];
vector<node> g[maxn];
inline void kruskal()
{
	n=read();m=read(); for(rll i=1;i<=n;i++) fa[i]=i;
	for(rll i=1,u,v,w;i<=m;i++) u=read(),v=read(),w=read(),g[u].push_back((node) { NULL,v,w,i }),g[v].push_back((node) { u,w,i }),val[i]=(node){u,v,w,i};
	sort(val+1,val+m+1);
	for(rll i=1,fx,fy;i<=m;i++)
	{
		fx=find(val[i].x),fy=find(val[i].y);
		if(fx^fy) fa[fx]=fy,tot++,sel[val[i].id]=1;
		if(tot==n-1) break;
	}
}

拓扑排序

拓扑排序是将某些有先后顺序完成的点们按照这种顺序输出.

比如某个点需要完成前面的哪些才可以去完成.

例题:家谱树

有个人的家族很大,辈分关系很混乱,请你帮整理一下这种关系.
给出每个人的孩子的信息.
输出一个序列,使得每个人的后辈都比那个人后列出.

首先把图读进来,然后每一次选择一个没有直接前驱的点输出,并将其从图中删除. 重复这个操作直至图中无点,这时拓扑序就形成了.

#include<bits/stdc++.h>
#define ll int
#define rg register
#define rll rg ll
#define maxn 5001
#define put_ putchar(' ')
#define putn putchar('\n')
using namespace std;
inline ll read()
{
	rll f=0,x=0;rg char ch=getchar();while(ch<'0'||ch>'9') f|=ch=='-',ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar();return f?-x:x;
}
inline void write(rll x) { if(x<0) putchar('-'),x=-x;if(x>9) write(x/10);putchar(x%10|'0'); }
ll t,n,mx,num;
vector<ll> g[maxn];
ll rudu[maxn];
queue<ll> q;
int main()
{
	n=read();
	for(rll i=1,a;i<=n;i++) while(a=read()&&a) g[i].push_back(a),rudu[a]++;
	for(rll i=1;i<=n;i++) if(!rudu[i]) q.push(i);
	while(!q.empty())
	{
		rll t=q.front();q.pop();
		for(rll i=0;i<g[t].size();i++)
		{
			rudu[g[t][i]]--;
			if(!rudu[g[t][i]]) q.push(g[t][i]);
		}
		write(t),put_;
	}
	return 0;
}
posted @ 2022-10-05 16:14  1Liu  阅读(43)  评论(0)    收藏  举报