【算法每日一练]-图论(保姆级教程篇7 最小生成树 ,并查集模板篇)#村村通 #最小生成树

目录

题目:村村通

并查集 

题目:最小生成树

kruskal算法

prim算法


        

先引入问题:

要在n个城市之间铺设光缆,主要目标是要使这 n 个城市的任意两个之间都可以通信,但铺设光缆的费用很高,且各个城市之间铺设光缆的费用不同,因此另一个目标是要使铺设光缆的总费用最低。这就需要找到带权的最小生成树

说白了就是将此图连通起来的最小代价。

        

对于一个有N个点的图,边一定是大于等于N-1条的。图的最小生成树,就是在这些边中选择N-1条出来,连接所有的N个点。这N-1条边的边权之和是所有方案中最小的

有两种算法:prim和kruskal

前者适合稠密图,后者适合稀疏图(不然炸你内存)

        

        

要先说并查集才行

题目:村村通

        

并查集 

【并查集思想】:是集合。一个是并操作(建树),一个是查操作(查树)。并操作是将一个集合的树变成另一个集合树的子树。

        
我们只需要建和原图等价的并查树即可,根本不用建原图
查操作是从该元素开始查找父节点直到找到根节点看看是否相同

        
1,初始化每个点的父亲为自身
2,并操作:(建边)合并两个集合的树根(祖宗)(查的过程中并路径压缩)
3,查操作:最后查找有几个祖宗即可

#include <bits/stdc++.h>              
using namespace std;
int fa[1000001], n, m, x, y;
int find(int x)
//找到祖先后并修改中间点的fa(路径压缩使更快的查到祖宗,
//其实就是对树进行优化,减少了树的深度,效果是将多代变成一代) 
{if(x!=fa[x]) fa[x]=find(fa[x]);
//自己不是祖宗,直接更新成亲爹的祖宗号
//但是如果是dp,那就要先保存原亲爹号,不然你就找不到爹了(路径压缩的代价)return fa[x];//返回祖先 
}
void unity(int x, int y)
{int f1=find(x);//如果x和y本来就在同一个集合完全 不影响int f2=find(y);fa[f1]=f2;//合并树根 
}
int main()
{while(true){int ans=0;cin>>n>>m;if(n==0) return 0;for(int i=1; i<=n; i++){fa[i]=i;//先初始化成节点}for(int i=1; i<=m; i++){scanf("%d %d", &x, &y);//合并<x,y>能到的地方unity(x,y);//建边,建树}for(int i=1; i<=n; i++){//一共有几个祖宗if(find(i)==i) ans++;}printf("%d\n", ans-1);//共需修ans-1条路即可}return 0;
}

         

         

题目:最小生成树

         

kruskal算法

【kruskal】:贪心的每次取最小权值的边进行合并(只要不构成环),当恰好合并了n-1条边时候就是最小生成树。只要小于就不是,此图也不连通
可以使用并查集来实现合并和不构成环

                
kruskal甚至不需要建图,但是如果是完全图的话,存边容易MLE,这时候就要prim

#include<bits/stdc++.h>
using namespace std;
int f;
struct Edge{ int u,v,w; }e[200005];
int fa[5005],n,m,ans,cnt;bool cmp(Edge a,Edge b){ return a.w<b.w;}int find(int x)
{if(x!=fa[x]) fa[x]=find(fa[x]);return fa[x];//返回祖先 
}void kruskal()
{sort(e+1,e+1+m,cmp);//将边的权值排序for(int i=1;i<=m;i++){int fu=find(e[i].u), fv=find(e[i].v);if(fu==fv) continue;  //若出现两个点已经联通了,则说明这一条边不需要了ans+=e[i].w; //将此边权计入答案fa[fv]=fu; //合并操作if(++cnt==n-1)//如果边数恰好为n-1,则说明最小生成树已经建成{f=1;break;}}
}int main()
{cin>>n>>m;for(int i=1;i<=n;i++) fa[i]=i;//初始化并查集节点for(int i=1;i<=m;i++){scanf("%d %d %d",&e[i].u,&e[i].v,&e[i].w);}kruskal();if(f==1)printf("%d",ans);else cout<<"orz";//不连通return 0;
}

         

prim算法

【prim算法】:prim算法基于贪心,我们每次总是选出一个离生成树距离最小的点去加入生成树,最后实现最小生成树(不做证明,理解思想即可)

每次都最小生成数和dijkstra思想很像,都是从小图开始,每次都从周围合并一个最小的点然后不断扩大,所以长得也很像,感觉完全一样啊

#include <bits/stdc++.h>
using namespace std;
int k,n,m,cnt,sum;
int head[5005],dis[5005],vis[5005];
typedef pair <int,int> pii;
struct Edge{ int v,w,next;}e[400005];void add(int u,int v,int w){e[++k]=(Edge){v,w,head[u]};head[u]=k;}void prim()
{priority_queue <pii,vector<pii>,greater<pii> > q;memset(dis,0x3f,sizeof(dis));dis[1]=0;//dis是周围点到集合的最小距离q.push(make_pair(0,1));while(!q.empty()&&cnt<n)//cnt是已经加入的点数{int d=q.top().first,u=q.top().second;//取出周围最小dis的点q.pop();if(vis[u]) continue;cnt++;sum+=d;vis[u]=1;//标记此点已经加入for(i=head[u];i;i=e[i].next){int ve=e[i].v,vw=e[i].w;//到集合最小距离就是权值if(vw<dis[ve])//如果变小就更新入队,以便获取最小的点dis[ve]=vw,q.push(make_pair(dis[ve],ve));}}
}int main()
{int u,v,w;scanf("%d%d",&n,&m);for(i=1;i<=m;i++){scanf("%d%d%d",&u,&v,&w);add(u,v,w);add(v,u,w);}prim();if (cnt==n)printf("%d",sum);else printf("orz");//如果小于n说明不连通
}

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

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

相关文章

C++ day37 贪心算法 单调递增的数字 监控二叉树

题目1&#xff1a;738 单调递增的数字 题目链接&#xff1a;单调递增的数字 对题目的理解 返回小于或等于n的最大数字&#xff0c;且数字是单调递增&#xff08;单调递增数字的定义&#xff1a;每个相邻位上的数字满足x<y&#xff09; 贪心算法 注意本题的遍历顺序是从…

羽隔已就之图像处理之BP神经网络入门

小y最近非常忙&#xff0c;这一年来&#xff0c;活很多&#xff0c;一直在加班、出差&#xff0c;也没好好休息过。最近在武汉出差一个多月了&#xff0c;项目逐渐完结&#xff0c;有点闲时间了&#xff0c;回首望&#xff0c;这一年设定的很多目标都没完成。 还记得&#xff0…

关于Unity中字典在Inspector的显示

字典在Inspector的显示 方法一&#xff1a;实现ISerializationCallbackReceiver接口 《unity3D游戏开发第二版》记录 在编辑面板中可以利用序列化监听接口特性对字典进行序列化。 主要继承ISerializationCallbackReceiver接口 实现OnAfterDeserialize() OnBeforeSerialize() …

动态规划--使用最小花费爬楼梯

题目描述 给你一个整数数组 cost &#xff0c;其中 cost[i] 是从楼梯第 i 个台阶向上爬需要支付的费用。一旦你支付此费用&#xff0c;即可选择向上爬一个或者两个台阶。 你可以选择从下标为 0 或下标为 1 的台阶开始爬楼梯。 请你计算并返回达到楼梯顶部的最低花费。 示例…

【FGPA】Verilog:JK 触发器 | D 触发器 | T 触发器 | D 触发器的实现

0x00 JK 触发器 JK 触发器是 RS 触发器和 T 触发器的组合&#xff0c;有两个输入端 J 和 K&#xff0c;如果两个输入端都等于 1&#xff0c;则将当前值反转。 行为表 状态图 Timing Diagram Circuit JK 触发器的设计目的是防止 RS 触发器在输入 S 和 R 均等于 …

2020年1月31日 Go生态洞察:pkg.go.dev的未来步骤

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文…

安卓开发学习---kotlin版---笔记(一)

Hello word 前言&#xff1a;上次学习安卓&#xff0c;学了Java开发&#xff0c;简单的搭了几个安卓界面。这次要学习Kotlin语言&#xff0c;然后开发安卓&#xff0c;趁着还年轻&#xff0c;学点新东西&#xff0c;坚持~ 未来的你会感谢现在努力的你~ 主要学习资料&#xff1a…

Day12 qt QMianWindow,资源文件,对话框,布局方式,常用ui控件

QMianWindow 概述 QMainWindow 是一个为用户提供主窗口程序的类&#xff0c;包含一个菜单栏&#xff08; menu bar &#xff09;、多 个工具栏 (tool bars) 、多个铆接部件 (dock widgets) 、一个状态栏 (status bar) 及 一个中心部件 (central widget) 许多应用程序的基础…

matlab配置

matlab配置 windowslinux挂载安装MATLAB windows 按照这里一步步配置就行( 移动硬盘中软件备份中自取) linux linux配置步骤 挂载 sudo mount -t auto -o loop /media/oyk/Elements/ubuntu/MATLAB/R2017a_glnxa64_dvd1.iso ./matlab/安装MATLAB 挂载完成后&#xff0c;先…

无人机光伏巡检代替人工,贵州电站运维升级

无人机光伏巡检如何做到降本增效&#xff1f;贵州省光伏电站有新招&#xff01;某70MWp的光伏电站通过引入复亚智能无人机光伏巡检系统&#xff0c;专注于使用无人机对区域内的光伏面板进行自动巡航巡查&#xff0c;利用自动化巡检和故障识别技术&#xff0c;显著提升了光伏电站…

vue中:计算属性computed

1. 在computed中定义计算属性方法根据已有的数据进行计算返回一个要显示的新数据 2. 在页面中使用{{计算属性名}}来显示返回的数据 3. computed: 内部有缓存, 多处读取只计算一次 4. 计算属性默认相当于只有getter来根据已有数据计算返回一个新数据值, 也可以指定setter来监…

pandas(八)--实战一下

背景 收到一批数据&#xff0c;数据形式。采集数据的间隔时间是10分钟&#xff0c;全天采集数据&#xff0c;每天的数据量是144条 处理后的数据形式 分析 去除表格中的q的异常值&#xff0c;置为0去除重复行将原始表格中的date分裂成日期和时间缺失的时间点数据补0&#x…