洛咕博客地址: − > 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.