引子

啊啊额,从一张图里抽出几条边,组成一棵树,无环n−1n-1n1条边,就是生成树。那么边权和最小的生成树就叫最小生成树,最大生成树同理。

kruskal最小生成树

要求kruskal最小生成树,我们首先用结构体数组接入数据,然后按照每条边的边权进行升序排序,接着用并查集判断每一条边,看两边的端点是否已经在目前的生成树里连通,否的话就加入这条边,如果目前边数==n−1==n-1==n1,就breakbreakbreak,在循环外再进行判断,看并查集数组里面是否有两个以上的不同的祖宗,是的话就按题目要求输出,否则输出目前disdisdis数组的总和

例子

3
1
4
2
1
3
3
A
B
C
D
E

如上图,我们可以知道有以下7条边:

  1. A↔\leftrightarrowB,w=3
  2. A↔\leftrightarrowC,w=1
  3. A↔\leftrightarrowE,w=1
  4. B↔\leftrightarrowD,w=4
  5. C↔\leftrightarrowD,w=2
  6. C↔\leftrightarrowE,w=3
  7. D↔\leftrightarrowE,w=3

排完序是这样:

  1. A↔\leftrightarrowC,w=1
  2. A↔\leftrightarrowE,w=1
  3. C↔\leftrightarrowD,w=2
  4. A↔\leftrightarrowB,w=3
  5. C↔\leftrightarrowE,w=3
  6. D↔\leftrightarrowE,w=3
  7. B↔\leftrightarrowD,w=4

接下来我们来枚举每一条边,具体过程如下:

  1. A↔\leftrightarrowB,此时为空图,直接放进去。
1
A
C
  1. A↔\leftrightarrowE,加入后并不形成环,可以放进去。
1
1
A
C
E
  1. C↔\leftrightarrowD,放入。
1
1
2
A
C
E
D
  1. A↔\leftrightarrowB,也可以放入。
1
1
2
3
A
C
E
D
B

此时,我们已经添加了n−1n-1n1条边,已经breakbreakbreak了,所以答案算出最终为7。

实现 //////这一段每一行都有注释

//就不给include了
struct edge{//结构体int u,v,w;//来点,去点,边权
}a[200005];//结构体数组
bool cmp(edge x,edge y){//结构体排序return x.w<y.w;//按照边权排序
}//一个用于排序的函数
int fa[5005],cnt,ans; //并查集数组,已选边数,边权总和
int find(int x){ //并查集必备return fa[x]==x?x:fa[x]=find(fa[x]);//顺带更新fa数组
}//一个用于并查集的函数
int kruskal(){//开始最小生成树int n,m;//点的数目,边的数目cin>>n>>m;//输入数据for(int i=1;i<=m;i++){//输入每条边cin>>a[i].u>>a[i].v>>a[i].w;//接入结构体数组}//第一个循环出现了sort(a+1,a+1+m,cmp);//排序for(int i=1;i<=n;i++)fa[i]=i;//并查集数组的初始值for(int i=1;i<=m;i++){//循环每条边int u=a[i].u,v=a[i].v,w=a[i].w;//拿出a[i]int u=find(u),v=find(v);//跑一边并查集if(u!=v){//如果祖先不相同fa[v]=u,cnt++,ans+=w;//v认u做祖宗,边数加一,总和加该边的边权}//这是一个ifif(cnt==n-1){//如果边数达到了n-1break;//退出循环}//这也是一个if}//还有一个循环int sum=0;//查看有几个祖宗for(int i=1;i<=n;i++){//查看并查集数组if(i==fa[i])sum++;//如果这货认自己为祖宗,说明他就是一个祖宗}//还有最后的判断if(sum>=2){//如果有两个及以上个祖宗,就没戏了return -1;//看题目要输出什么就输出什么}//快结束了return ans;//返回最终结果
}//不要抄袭哦!

kruskal还没有结束,他还有时间复杂度,O(mlogmmlogmmlogm)。//////华丽结束

prim最小生成树

额额啊,你没猜错,还有prim!让我们开始吧!
首先,他非常 常见 好写 简单 易懂;然后实现有点像dij,毕竟都是图论;最后,他是一种基于贪心的算法。为什么这么说呢?因为他每次都是找出一个离生成树距离最小的一个点,然后把该点放入生成树。

过程&例子

俗话说的好,一张图顶一堆话……

3
1
4
2
1
3
3
1
2
3
4
5

dis数组={0,\infty, \infty,\infty,\infty$}

还是这图,但我们这次用prim来写。

最开始,我们把1号点放进生成树,此时生成树和dis长这样:

1

dis数组={0,∞,∞,∞,∞0,\infty, \infty,\infty,\infty0} //////第0轮

然后循环n-1次,每次找出dis数组里值最小且没被选过的一个点,把它拿出来看看他有哪些边,如果连向的点也没被选过,就更新他的dis。生成树和dis依次如下:

1

dis数组={0,3,1,∞,10,3, 1,\infty,10311} //////第1轮

1
1
3

dis数组={0,3,1,2,10,3, 1,2,103121} //////第2轮

1
1
1
3
5

dis数组={0,3,1,2,10,3, 1,2,103121} //////第3轮

1
1
2
1
3
5
4

dis数组={0,3,1,2,10,3, 1,2,103121} //////第4轮

最终答案为7。

实现

const int d=1e9;//朴素
struct edge{int u,v,w;
};
vector<edge> E[5005];
int dis[5005],n,m;
bool f[5005];
int prim(){for(int i=1;i<=n;i++)dis[i]=d;dis[1]=0;//千万别忘了for(int i=1;i<n;i++){int mn=d,u=0;for(int k=1;k<=n;k++){if(dis[k]<mn&&!f[k]){mn=dis[k],u=k;}} f[u]=1;//把自己加入生成树中for(int j=0;j<E[u].size();j++){int v=E[u][j].v,w=E[u][j].w;if(!f[v]){dis[v]=min(dis[v],w);}}}int sum=0;for(int i=1;i<=n;i++){if(dis[i]==d){return -1;}sum+=dis[i];}return sum;
}
const int dd=1e9;//堆优化
struct node{int v,w;bool operator<(const node&a)const{return w>a.w;}
};
int n,m,dis[5005];
bool vis[5005];
vector<node> E[5005];
priority_queue<node> q;
void prim(){int cnt=0;q.push({1,0});while(!q.empty()&&cnt<=n){node d=q.top();q.pop();if(vis[d.v])continue;vis[d.v]=1,dis[d.v]=min(dis[d.v],d.w),cnt++;for(int i=0;i<E[d.v].size();i++){if(!vis[E[d.v][i].v]){q.push({E[d.v][i].v,E[d.v][i].w});}}}
}//知道为什么跟dij很像了吧!

等,时 度!

朴素 O(n2+m^2 + m2+m)
堆优化 O((n+m)logm(n+m)logm(n+m)logm)

kruskal重构树

啊额啊,生成树之后还有重构树,要炸掉了 ! 可恶的kruskal !

重构树,说白了就是把一张图改成一棵树,然后再这棵树上LCA。

过程&例子

嗯?不懂?好吧,我们还是一样来张图:

1
5
3
1
2
4
1
2
3
4
5

首先跟前面kruskal最小生成树的步骤一样,把所有的边按照边权排序。

  1. 1 ↔\leftrightarrow 2,w=1
  2. 2 ↔\leftrightarrow 4,w=1
  3. 3 ↔\leftrightarrow 5,w=2
  4. 1 ↔\leftrightarrow 4,w=3
  5. 5 ↔\leftrightarrow 4,w=4
  6. 1 ↔\leftrightarrow 3,w=5

接着,我们循环把所有的边拿出来,查看他们的祖宗是否相同,否的话就把这条边挂在用于LCA的树上。

咋挂?这时,我们需要扩域,也就是建造虚拟节点,cnt。

循环前把cnt设为n,每次要挂边时,cnt++,然后让cnt当u和v的父亲,同时更新并查集数组和记录每个cnt的左儿子节点和右儿子节点的距离的b数组。

以下为得出的树和各个数组:

6
1
2
7
4
8
3
5
9
123456789
fa668787999
b/////1124

现在树建好了,就等着LCA啦!

实现

#include<bits/stdc++.h>
using namespace std;
struct edge{int u,v,w;
}a[600005];
bool cmp(edge x,edge y){return x.w<y.w;
}
int fa[200005],b[200005],dep[200005],fc[25][200005],cnt;//并查集数组,每个cnt的左儿子节点和右儿子节点的距离,深度,次方父亲数组
vector<int> E[200005];
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);
}
void dfs(int x,int f){dep[x]=dep[f]+1;for(int i=1;i<=20;i++)fc[i][x]=fc[i-1][fc[i-1][x]];for(int i=0;i<E[x].size();i++){int v=E[x][i];if(v==f)continue;fc[0][v]=x;dfs(v,x);}
}
int LCA(int x,int y){if(dep[x]>dep[y]){swap(x,y);}for(int i=19;i>=0;i--){if(dep[fc[i][y]]>=dep[x]){y=fc[i][y];}}if(x==y)return x;for(int i=19;i>=0;i--){if(fc[i][x]!=fc[i][y]){x=fc[i][x];y=fc[i][y];}}return fc[0][x];
}
int main(){int n,m;cin>>n>>m;cnt=n;for(int i=1;i<=m;i++){cin>>a[i].u>>a[i].v>>a[i].w;}sort(a+1,a+1+m,cmp);for(int i=1;i<=n*2;i++)fa[i]=i;//由于进行了扩域,所以*2for(int i=1;i<=m;i++){//把所有边拿出来瞅瞅int u=a[i].u,v=a[i].v,w=a[i].w;u=find(u),v=find(v);if(u!=v){fa[v]=fa[u]=++cnt;b[cnt]=w;E[u].push_back(cnt);E[v].push_back(cnt);E[cnt].push_back(u);E[cnt].push_back(v);}}for(int i=1;i<=cnt;i++){//一系列LCAif(i==fa[i]){dfs(i,0);}}int q;cin>>q;while(q--){//q次询问int x,y;cin>>x>>y;if(find(x)!=find(y)){cout<<"impossible"<<endl;}else{cout<<b[LCA(x,y)]<<endl;}}return 0;
}

俗话说得好,所有的代码都可以缩成两行。

#include<bits/stdc++.h> //献上最后的代码
using namespace std;struct edge{int u,v,w;}a[600005];vector<int> E[200005];int fa[200005],b[200005],dep[200005],fc[25][200005],cnt,n,m,q;bool cmp(edge x,edge y){return x.w<y.w;}int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}void dfs(int x,int f){for(int i=1;i<=20;i++){fc[i][x]=fc[i-1][fc[i-1][x]],dep[x]=dep[f]+1;}for(int i=0;i<E[x].size();i++){int v=E[x][i];if(v==f){continue;}else{fc[0][v]=x,dfs(v,x);}}}int LCA(int x,int y){if(dep[x]>dep[y])swap(x,y);for(int i=20;i>=0;i--){if(dep[fc[i][y]]>=dep[x])y=fc[i][y];}if(x==y)return x;for(int i=20;i>=0;i--){if(fc[i][x]!=fc[i][y])x=fc[i][x],y=fc[i][y];}return fc[0][x];}int main(){cin>>n>>m;for(int i=1;i<=m;i++){cin>>a[i].u>>a[i].v>>a[i].w;}sort(a+1,a+1+m,cmp);for(int i=1;i<=n*2;i++)fa[i]=i,cnt=n;for(int i=1;i<=m;i++){int u=a[i].u,v=a[i].v,w=a[i].w,fu=find(u),fv=find(v);if(fu!=fv)fa[fu]=fa[fv]=++cnt,b[cnt]=w,E[cnt].push_back(fu),E[cnt].push_back(fv),E[fu].push_back(cnt),E[fv].push_back(cnt);}for(int i=1;i<=cnt;i++)if(fa[i]==i)dfs(i,0);cin>>q;while(q--){int x,y;cin>>x>>y;find(x)!=find(y)?cout<<"impossible\n":cout<<b[LCA(x,y)]<<endl;}return 0;}

完结万岁!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/93080.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/93080.shtml
英文地址,请注明出处:http://en.pswp.cn/web/93080.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据大集网:实体店获客引流的数字化引擎,解锁精准拓客新密码​

​在实体店面临流量焦虑、获客成本攀升的当下&#xff0c;实体店获客引流工具的重要性愈发凸显。如何在激烈的市场竞争中精准触达目标客户、构建可持续的客流增长模式&#xff1f;数据大集网凭借其创新的智能获客体系与全链路服务能力&#xff0c;正成为万千实体店突破增长瓶颈…

nginx --ssl证书生成mkcert

github https://github.com/FiloSottile/mkcert/releases网盘下载地址 https://pan.baidu.com/s/1XI0879pqu7HXZMnmQ9ztaw 提取码: 1111windows使用示例

守拙以致远:个人IP的长青之道|创客匠人

2025年被认为是AI应用全面爆发的一年。各种人工智能工具在写作、制图、剪辑等领域广泛使用&#xff0c;大大提升了个人和团队的工作效率。对于个人IP而言&#xff0c;这类工具的出现确实带来了新的机会&#xff0c;但也伴随着一种现象——一些人开始过度依赖甚至神化AI&#xf…

USB 3.0 LTSSM 状态机

USB2.0在电源供应后&#xff0c;通过Pull Up D-来决定枚举LS&#xff0c;Pull Up D有一个USB高速握手过程&#xff0c;来决定HS FS。USB3.0则会通过链路训练&#xff08;Link Training&#xff09;&#xff0c;来准备USB3.0通信。每当我们插上USB线的时候&#xff0c;对于3.0的…

MySQL窗口函数与PyMySQL以及SQL注入

MySQL窗口函数与PyMySQL实战指南&#xff1a;从基础到安全编程 引言 在数据处理和分析领域&#xff0c;MySQL作为最流行的关系型数据库之一&#xff0c;其窗口函数功能为数据分析提供了强大的支持。同时&#xff0c;Python作为数据分析的主要语言&#xff0c;通过PyMySQL库与My…

高级项目——基于FPGA的串行FIR滤波器

给大家安利一个 AI 学习神站&#xff01;在这个 AI 卷成红海的时代&#xff0c;甭管你是硬核开发者还是代码小白&#xff0c;啃透 AI 技能树都是刚需。这站牛逼之处在于&#xff1a;全程用 "变量名式" 幽默 生活化类比拆解 AI&#xff0c;从入门到入土&#xff08;啊…

JPrint免费的Web静默打印控件:PDF打印中文乱码异常解决方案

文章目录JPrint是什么&#xff1f;中文乱码&#xff08;Using fallback font xxx for xxxx&#xff09;1.字体嵌入2.客户机字体安装开源地址相关目录导航使用文档端口号修改代理使用场景打印服务切换中文乱码解决方案 JPrint是什么&#xff1f; JPrint是一个免费开源的可视化静…

MFT 在零售行业的实践案例与场景:加速文件集成与业务协作的高效方案

零售行业竞争激烈、数字化转型迭代迅速&#xff0c;业务对数据与档案的传输、处理和整合要求极高。无论是新品上市市场数据&#xff0c;还是供应链物流单据&#xff0c;集成方式不论是通过API或是档案传输, 对于传输的稳定性,安全性与性能, 都会直接影响决策效率与顾客体验。MF…

OSG+Qt —— 笔记1 - Qt窗口加载模型(附源码)

🔔 OSG/OsgEarth 相关技术、疑难杂症文章合集(掌握后可自封大侠 ⓿_⓿)(记得收藏,持续更新中…) OSG+Qt所用版本皆为: Vs2017+Qt5.12.4+Osg3.6.5+OsgQt(master) 效果 代码(需将cow.osg、reflect.rgb拷贝至工程目录下) OsgForQt.ui main.cpp

开源安全云盘存储:Hoodik 实现端到端数据加密,Docker快速搭建

以下是对 Hoodik 的简单介绍&#xff1a; Hoodik 是一个使用 Rust 和 Vue 开发的轻量级自托管安全云存储解决方案采用了非对称RSA密钥对和AES混合加密策略&#xff0c;从文件存储加密到数据链路加密&#xff0c;全程保证数据安全支持Docker一键私有部署&#xff0c;数据和服务…

[C++] Git 使用教程(从入门到常用操作)

1. Git 简介 Git 是一款分布式版本控制系统&#xff0c;用来跟踪文件变化、协作开发、管理项目版本。 它是开源的&#xff0c;由 Linus Torvalds 在 2005 年开发&#xff0c;广泛用于开源与企业项目中。 2. 安装 Git Windows 前往 Git 官网 下载并安装。 安装时建议勾选 Git…

实盘回测一体的期货策略开发:tqsdk获取历史数据并回测,附python代码

原创内容第969篇&#xff0c;专注AGI&#xff0c;AI量化投资、个人成长与财富自由。 星球好多同学希望说说实盘&#xff0c;我们就从实盘开始吧。 我们选择tqsdk给大家讲解&#xff0c;tqsdk支持免费注册&#xff0c;使用模拟账户&#xff0c;历史和实时数据&#xff0c;方便…

大模型推理框架vLLM 中的Prompt缓存实现原理

背景&#xff1a;为什么需要Prompt缓存模块&#xff1f;在大模型问答多轮对话应用场景中&#xff0c;不同请求的 Prompt 往往有相同的前缀&#xff0c;比如&#xff1a;第一次问答&#xff1a;你是一名专业的电子产品客服&#xff0c;负责回答客户关于手机产品的咨询。请根据以…

Python之Django使用技巧(附视频教程)

概述 Django 是一个高级的 Python Web 框架&#xff0c;遵循 “batteries-included”&#xff08;内置电池&#xff09;理念&#xff0c;提供了构建 Web 应用所需的大部分组件&#xff0c;让开发者可以专注于业务逻辑而不是底层细节。视频教程&#xff1a;https://pan.quark.cn…

sqli-labs通关笔记-第44关 POST字符型堆叠注入(单引号闭合 手工注入+脚本注入3种方法)

目录 一、堆叠注入 二、源码分析 1、代码审计 2、SQL注入安全性分析 三、堆叠手注法 1、进入靶场 2、正确用户名密码登录 3、堆叠注入 4、查看数据库 四、联合手注法 1、获取列数 2、确认回显位 3、获取数据库名 4、获取表名 5、获取列名 6、获取字段 7、总结…

从深度伪造到深度信任:AI安全的三场攻防战

前言当大模型开始“睁眼”看世界&#xff0c;伪造者也开始“闭眼”造世界。2025 WAIC释放出的信号很明确&#xff1a;没有AI安全底座&#xff0c;就没有产业智能化的高楼。WAIC 把“安全”摆在与“创新”同等重要的位置&#xff0c;形成了“1 份共识框架&#xff0b;2 份重磅报…

【C++】哈希的应用:位图和布隆过滤器

目录 一、位图 1.1 位图的概念 1.2 位图的实现 1.3 位图的应用 二、布隆过滤器 2.1 布隆过滤器的提出 2.2 布隆过滤器的概念 2.3 布隆过滤器的插入和查找 2.4 布隆过滤器的删除 2.5 布隆过滤器的优点 2.6 布隆过滤器的缺点 一、位图 1.1 位图的概念 1. 面试题 给4…

C语言:指针(4)

1. 回调函数回调函数就是指通过函数指针调用的函数。如果将函数指针作为参数传递给另一个函数&#xff0c;另一个函数根据指针来调这个函数&#xff0c;那么被调用的函数就是回调函数。回调函数不是由这个函数的实现方直接调用&#xff0c;而是在特定的条件下由另一方调用的。例…

vue--video使用动态src时,视频不更新

问题描述 在 Vue项目中&#xff0c;尝试动态更新 标签的 元素 src 属性来切换视频时&#xff0c;遇到了一个问题&#xff1a;即使 src 已更改&#xff0c;浏览器仍不显示视频。 <template><video width"100%" height"100%" controlspause"…

计算机视觉--opencv(代码详细教程)(一)

在计算机视觉的广袤领域中&#xff0c;OpenCV 是一座极为关键的里程碑。无论是在前沿的学术研究&#xff0c;还是在蓬勃发展的工业界&#xff0c;OpenCV 凭借其强大的功能与高效的性能&#xff0c;为开发者提供了丰富的图像处理和计算机视觉算法&#xff0c;助力无数项目落地。…