IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> Kruskal重构树 -> 正文阅读

[数据结构与算法]Kruskal重构树

用处:求有关两点间路径最大边权最小值和最小边权最大值问题

以求两点路径最大边权最小值为例(n个节点)(以下性质及方法均只适用于按照求两点路径最大边权最小值的方法所重构的树)

性质:

每两个点的最近公共祖先的权值就是两点间路径最大边权最小值(边权从小到大排序)

重构得到的图是一个大根堆(前提原来图是连通的)

重构之后的树是2*n-1个节点,且节点编号为1~n的节点没有点权,而n+1~2*n-1的节点具有点权且点权为原来图中最小生成树的边权

方法:

首先先将图中所有边权从小到大排序,选取一条边,如果两点不在一个集合中,就新建一个节点,并将这两个点分别与新建节点连一条边,并将原来两点之间的边权作为新建节点的点权,就这样遍历完所有的边即可得到Kruscal重构树。

非常重要:对树进行dfs时一定要从根节点开始,因为这个树可以看成一个堆,他是有方向的。

注意:如果原来给的图是连通的,那么经过重构后的图就是一棵树,否则就是一个森林

这个方法常与最近公共祖先一块用,因为两个点之间的最近公共祖先的点权是两点在原图中的最大边权的最小值,所以这个知识学习的前缀知识就是会求解最近公共祖先,如果有不了解的小伙伴可以看下我之前写的博客,这里附上博客链接:最近公共祖先(LCA)(树上倍增)_AC__dream的博客-CSDN博客

下面给出一道例题:

题目链接:[NOIP2013 提高组] 货车运输 - 洛谷

分析:求每辆车在不超过车辆限重的情况下,最多能运多重的货物,因为我们载重的货物不能超过任何一条边权,所以本题显然是要求最小边权的最大值,那么我们就直接将原图的边权从大到小排序,然后用kruscal重构即可。但是因为本题给出的图不一定连通,所以需要单独判断一下两个点是否在一棵树中,如果不在一棵树中直接输出-1,否则输出两点最近公共祖先的点权即可。判断是否在同一颗树中直接用并查集判断即可

先说一下本题需要注意的点

所给图不一定是连通图,所以我们需要一个一个点去遍历,但是一定要注意每次遍历都是从根节点开始的,所以我们需要倒着往前遍历,还需要用一个vis数组记录哪些点已经被遍历过了,这是因为我们在重构树时点权大的点的标号大

下面是代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<queue>
using namespace std;
const int N=2e5+10;
int h[N],e[N],ne[N],w[N],idx,d[N];
int f[N][23],fu[N];
bool vis[N];
struct node{
	int x,y,z;
}p[N];
bool cmp(node a,node b)
{
	return a.z>b.z;
}
void add(int x,int y)
{
	e[idx]=y;
	ne[idx]=h[x];
	h[x]=idx++;
}
int find(int x)
{
	if(x!=fu[x]) fu[x]=find(fu[x]);
	return fu[x]; 
} 
void dfs(int x,int father)
{
	vis[x]=true;
	d[x]=d[father]+1;
	f[x][0]=father;
	for(int i=1;i<=20;i++)
		f[x][i]=f[f[x][i-1]][i-1];
	for(int i=h[x];i!=-1;i=ne[i])
	{
		int j=e[i];
		if(j==father) continue;
		dfs(j,x);
	}
}
int lca(int x,int y)//返回x和y最近公共祖先的权值 
{
	if(d[x]<d[y]) swap(x,y);
	for(int i=20;i>=0;i--)
		if(d[f[x][i]]>=d[y]) x=f[x][i];
	if(x==y) return w[x];
	for(int i=20;i>=0;i--)
		if(f[x][i]!=f[y][i])
			x=f[x][i],y=f[y][i];
	return w[f[x][0]];
}
int main()
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=2*n-1;i++)
		fu[i]=i,h[i]=-1;
	for(int i=1;i<=m;i++)
		scanf("%d%d%d",&p[i].x,&p[i].y,&p[i].z);
	sort(p+1,p+m+1,cmp);
	int t=n;
	for(int i=1;i<=m;i++)
	{
		int f1=find(p[i].x),f2=find(p[i].y);
		if(f1==f2) continue;
		++t;
		fu[f1]=fu[f2]=t;
		w[t]=p[i].z;
		add(f1,t);add(f2,t);
		add(t,f1);add(t,f2);
	}
	for(int i=t;i>=1;i--)//此处应保证每次遍历都从根节点开始遍历 
	{
		if(!vis[i])
			dfs(i,0);
	}
	int q;
	scanf("%d",&q);
	while(q--)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		if(find(u)!=find(v))//u和v不在一棵树上 
		{
			puts("-1");
			continue;
		}
		printf("%d\n",lca(u,v));
	}
	return 0;
}

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-03-06 13:22:01  更:2022-03-06 13:23:01 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/29 1:37:51-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码