【图论】【生成树】最小树形图

最小树形图
  • 有向图的最小生成树,从根节点能到所有节点,且每条边权值和最小。
  • 朱刘算法(Edmonds 算法), O ( n m ) O(nm) O(nm)
  • 模板题:给定包含n个结点,m条有向边的一个图。输出以结点r为根的最小树形图每条边的权值之和,如果没有以r为根的最小树形图,输出-1。
  • 步骤
    • 统计每个点的最小入边,加入答案
    • 如果有一个点找不到入边,说明不能生成最小树形图,返回-1
    • 每个点由最小入边找环缩点
    • 其余点不变,重构图,边权修改为原边权-到达节点最小入边
int in[N], pre[N]; //in[i]记录每轮i点入边中最小的边权,pre[i]记录最小边权对应的端点编号
int vst[N], scc[N];
int Zhuliu()
{
	int ans = 0;
	while (1)
	{
		for (int i = 1; i <= n; i++)
			in[i] = INF; // 初始化
		for (int i = 1; i <= m; i++)
		{
			int u = edge[i].from, v = edge[i].to;
			if (u != v && edge[i].val < in[v]) // 遍历所有边,对每个点找到最小的入边
				in[v] = edge[i].val, pre[v] = u;
		}
		for (int i = 1; i <= n; i++)	   // 判定无解
			if (i != root && in[i] == INF) //有一个点找不到入边了
				return -1;
		int sc = 0;
		for (int i = 1; i <= n; i++)
			vst[i] = scc[i] = 0;
		for (int i = 1; i <= n; i++)
		{
			if (i == root)
				continue;
			ans += in[i];
			int v = i;
			while (vst[v] != i && !scc[v] && v != root) // 找环,找回自己/找到一个缩过点的/找到根节点了都退出
			{
				vst[v] = i;
				v = pre[v];
			}
			if (!scc[v] && v != root) //一定是找回自己
			{
				scc[v] = ++sc; // 缩点
				for (int u = pre[v]; u != v; u = pre[u])
					scc[u] = sc;
			}
		}
		if (sc == 0)
			break; // 无环,得到解
		for (int i = 1; i <= n; i++)
			if (!scc[i])
				scc[i] = ++sc;
		for (int i = 1; i <= m; i++) //重构图
		{
			int u = edge[i].from, v = edge[i].to;
			edge[i].from = scc[u], edge[i].to = scc[v];
			if (scc[u] != scc[v])
				edge[i].val -= in[v]; // 修改边权
		}
		root = scc[root];
		n = sc;
	}
	return ans;
}

模板题:洛谷P4716:【模板】最小树形图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值