最小树形图——朱刘算法

洛咕博客地址: − > C l i c k H e r e < − ->Click Here<- >ClickHere<,求捧场


最近想找最小生成树的题做,奈何难度有限,点进的蓝题紫题都和“生成树”么得什么关系……于是就莫名找到了一道最小树形图的题,然后当然不会辣……

瞎学了一下,还是啥都不会,但毕竟咱们有时间,磨了一会儿总算是懂了一点……

进入正题好了……


定义

说实话我到现在都不是很懂定义……

大概就是给定一张有向图,指定根节点 r o o t root root,求出一棵有向树,使选出的边的权值和最小

首先肯定是要把所有点都选上的……不然这题目还有什么意义……

然后是有向树……说明根节点的入度肯定是 0 0 0,其他店的入度最多为 1 1 1,出度都无所谓,这点很明显

然后就是怎么求了……


朱-刘算法

姑且不要吐槽这个名字了……虽然有点奇怪

朱刘算法的核心是啥呢?又是贪心哦……

(为什么要说“又”呢?因为之前写的 E K EK EK也是贪心……)

首先根据定义我们可以发现,每个非根节点的入度最多且必定为 1 1 1,那么我们就对每条边的终点贪心地选择权值最小的一条边

此时,选出来的一些边并不一定会是一棵树,不过可能会有的出现,所以我们把这些环缩点(这里不用 T a r j a n Tarjan Tarjan,根据选中边从终点向起点就行),然后重复这个过程

不过要注意的是,我们在找环之前是会加上连向该点的边的权值的,如果要把连向该点的边换一条的话,需要加上的就不是这另一条边的权值,而是另一条边的权值减去原本的边的权值,这样不方便计算,所以我们先把边的权值减去原本的边的权值就行

这样说可能很混乱……代码见吧……


朱-刘算法求最小树形图 模板 \text{\huge朱-刘算法求最小树形图\quad 模板} -刘算法求最小树形图模板

bool ZhuLiu()
{
	while(1)
	{
		tot=0;//缩点后重置
		memset(bel,0,sizeof(bel));
		memset(f,0,sizeof(f));
		memset(minx,60,sizeof(minx));
		for(int i=1;i<=m;i++)
		{
			int u=e[i].u,v=e[i].v;
			if(u^v && minx[v]>e[i].dis)//对每条边的终点选一条连向它的权值最小的边
			{
				minx[v]=e[i].dis;
				faz[v]=u;//记录到终点的边的起点
			}
		}
		minx[rt]=0;
		for(int i=1;i<=n;i++)//枚举每个点,缩点
		{
			int u;
			if(minx[i]==inf) return 0;//如果没有连向它的边就跳过
			ans+=minx[i];//加上答案
			for(u=i;u!=rt && f[u]!=i && !bel[u];u=faz[u]) f[u]=i;
        //此处的f数组存储的是每个缩过的环内的“标志节点”,类似并查集,但在模板题只是提供了是否被查询过的信息
			if(u!=rt && !bel[u])//如果有环把环内每个点标上号
			{
				bel[u]=++tot;
				for(int v=faz[u];v!=u;v=faz[v]) bel[v]=tot;
			}
		}
		if(!tot) return 1;//没有的话代表是一颗树了,退出即可
		for(int i=1;i<=n;i++) if(!bel[i]) bel[i]=++tot;//把剩下的一些节点也缩了
		for(int i=1;i<=m;i++)
		{
			int w=minx[e[i].v];
			e[i].u=bel[e[i].u];
			e[i].v=bel[e[i].v];
			int u=e[i].u,v=e[i].v;
			if(u!=v) e[i].dis-=w;//把每条可能被选择的边减去当前选的最短边权值,等下如果要选择直接加上权值就行了
		}
		n=tot;
		rt=bel[rt];
	}
}

写的很渣,欢迎找 B u g Bug Bug


F i n . Fin. Fin.

你可能感兴趣的:(c++,最小树形图)