刷题记录:牛客NC17509挖沟[prim+kruskal算法详解]

news2024/11/24 12:30:28

传送门:牛客

题目描述:

胡队长带领HA实验的战士们玩真人CS,真人CS的地图由一些据点组成,现在胡队长已经占领了n个据点,为了方
便,将他们编号为1-n,为了隐蔽,胡队长命令战士们在每个据点出挖一个坑,让战士们躲在坑里。由于需要在任意两
个点之间传递信息,两个坑之间必须挖出至少一条通路,而挖沟是一件很麻烦的差事,所以胡队长希望挖出数量尽可
能少的沟,使得任意两个据点之间有至少一条通路,顺便,尽可能的∑d[i][j]使最小(其中d[i][j]为据点i到j的距离)。
输入:
2 2
1 2 1
1 2 3
输出:
1

一道最小生成树的板题.接下来就就借此较为详细的介绍一下最小生成树经典的两种算法,prim和kruskal

首先介绍一下什么是最小生成树,就是给你一张图然后求出联通每一个点的边权值和最小的一张联通图,因为边权值和最小,所以显然最后是没有环的,所以也就变成了一棵树,这个求解的过程就叫做最小生成树.

首先介绍一下 k r u s k a l kruskal kruskal算法,该算法理解起来比较简单

首先该算法基于边进行操作,复杂度瓶颈在于排序,为 m l o g m mlogm mlogm,和点数无关,所以在稀疏图中比较优秀

算法思想:

首先我们的 k r u s k a l kruskal kruskal的算法思想就是将我们现有的边经过排序,然后每一次都取出权值最小的一条边,然后判断一下会不会因为加入这条边形成环,如果不会的话就加入这条边,直到我们的边数等于点数-1,构成一棵树

正确性解释

因为最小生成树是一张联通图,那么显然的,他是由多个联通分量通过边进行连接的.现在我们简化一下我们的问题,假设我们现在有两个联通分量,那么我想将这两个联通分量联通需要干什么??显然就是找出我们的两个联通分量之间最短的一条边是吧.那么假设我们现在有三个联通分量(两两不连通),现在我们想联通这三个分量,我们应该怎么办,显然先是求出两两之间最短的一条边(因为联通分量可能包含了多个点,那么此时可能有多条边可以构成联通),然后此时我们就有三条边了,此时我们肯定是找出三条边之间最短的两条边是吧.那么现在将我们的联通分量的数量推广到n个(注意单个点我们此时也算联通分量),那么此时我们就变成了如何联通n个点.那么显然的,我们应该找出两两联通量之间最短的边,然后选取最短的 n − 1 n-1 n1条边即可

那么此时我们结合一下 k r u s k a l kruskal kruskal的实现过程,我们将边进行了一个排序,然后从小往大取边(并且这条边不会构成环,保证我们的边是不同的联通分量之间).这意味这什么呢,说明我们每一次取得边都是目前我们的所有联通量两两之间的最短边中的最短的边(此处十分重要,请理解后继续),那么此时选取的边显然是符合我们最短的 n − 1 n-1 n1条边中的一条的,因为都是最短的了,显然符合最短的 n − 1 n-1 n1条,所以正确性此时也就不难理解了

如果还是无法理解的话,我再讲详细一点,我们现在有n个点,那么显然我们现在选取了n个点之中最短的边,是符合我们的上述最短的n-1条边的情况的,那么此时我们有一个两个点的联通分量出现了,此时一共有n-1个联通分量,那么此时我们的下一条边既然是不允许我们的联通分量形成一个环,那就说明他只能用来联通两个联通分量,所以此时的话我们的这条边可以认作n-1个联通分量中最短的边(此时我们将问题化为联通n-1个联通分量,因为我们求出的是所有的边,所以此时边不变),那么此时最短的边,符合我们的n-2条最短边中的一条,然后逐渐递归下去,正确性也可以得到证明

下面是 k r u s k a l kruskal kruskal的实现代码:

//Kruskal
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <string.h>
#include <stack>
#include <deque>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define root 1,n,1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define maxn 1000000
#define ll_maxn 0x3f3f3f3f3f3f3f3f
const double eps=1e-8;
int n,m;
struct Node{
	int u,v,w;
}edge[maxn];
bool cmp(Node a,Node b) {
	return a.w<b.w;
}
int fa[maxn];
int find(int a) {
	if(a==fa[a]) return a;
	return fa[a]=find(fa[a]);
}
int main() {
	n=read();m=read();
	for(int i=1;i<=n;i++) fa[i]=i;
	for(int i=1;i<=m;i++) {
		edge[i].u=read();edge[i].v=read();edge[i].w=read();
	}
	sort(edge+1,edge+1+m,cmp);
	int cnt=0;int ans=0;
	for(int i=1;i<=m;i++) {
		int x=edge[i].u,y=edge[i].v;
		if(find(x)==find(y)) continue;
		cnt++;
		fa[find(x)]=find(y);
		ans+=edge[i].w;
		if(cnt==n-1) {
			break;
		}
	}
	if(cnt!=n-1) printf("-1\n");
	else cout<<ans<<endl;
	return 0;
}

然后是 p r i m prim prim部分:

首先正经的 p r i m prim prim是根据点来的,复杂度为 n 2 n^2 n2,但是因为朴素版的 p r i m prim prim的复杂度有点高,并且是以存边进行的,实现起来不太舒服,所以博主接下来的 p r i m prim prim是"不正经版的",我存的是点,然后用的类似于dijkstra的堆优化的方式实现的,如果没见过可以参考一下,实现起来很舒服,但是此时复杂度时Mlogn,跟边有了一点点关系

算法思想:

我们随便找一个点作为我们的最小生成树的根节点,然后遍历我们的邻接点,将所有点和**将这个点加入我们的树的花费(注意这一点和dij不一样)**存入我们的堆中,然后我们的小根堆每次顶出点和和花费,然后继续更新,直到有n个点为止

正确性证明:

在这里插入图片描述
来一张洛谷上某大佬的一张图,便于解释.那么对于上面的这张图来说,我们直接拿B点证明一下,其他点的证明和B点是一样的,类比一下即可.

对于我们的B点,此时我们发现BG的长度是目前队列中距离最小的(队列中此时应有AF,BC,BI),那么此时按照prim算法就应该直接将BG这条边当做我们的最小生成树的一条边了.接下来证明一下,经过前面的 k r u s k a l kruskal kruskal的算法证明中,我们提到n个联通分量之间最短的n-1边显然就是我们的最小生成树的一条边,又因为此时BG是目前队列中距离最小的一条边,那么就是我们最短的边,不妨设此时树中的点的邻接点记为vn(vn显然也包括了我们的G点),注意此时的vn是目前我们的树所能联通的所有点,那么对于我们的G来说有两种情况,要么我们的G还是和B相连,要么我们的G和其他点形成一个联通分量然后靠其他点和树联通,那么此时又因为形成了一个联通分量,所以显然换用G连接更为优秀.所以无论如何都是G与树连接时最优的.

理解起来可能有点费劲,如果之前碰过堆优化的dijkstra应该好理解一点,建议自己对着例图模拟几遍

至此,证明结束

下面是具体的代码部分:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <string.h>
#include <stack>
#include <deque>
using namespace std;
typedef long long ll;
#define inf 0x3f3f3f3f
#define root 1,n,1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
	ll x=0,w=1;char ch=getchar();
	for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
	return x*w;
}
#define maxn 1000000
#define ll_maxn 0x3f3f3f3f3f3f3f3f
const double eps=1e-8;
int n,m;
struct Node{
	int v,w;
};
struct heapnode{
	int u,d;
	bool operator<(const heapnode &rhs) const {
		return d>rhs.d;
	}
};
vector<Node>edge[maxn];
int dis[maxn];int vis[maxn];
void prim(int S) {
	memset(dis,0x3f,sizeof(dis));
	priority_queue<heapnode>q;
	q.push({S,0});
	dis[S]=0;
	int cnt=0;int ans=0;
	while(!q.empty()&&cnt<n) {
		heapnode f=q.top();q.pop();
		int u=f.u;
		if(vis[u]) continue;
		cnt++;ans+=dis[u];
		vis[u]=1;
		for(int i=0;i<edge[u].size();i++) {
			int v=edge[u][i].v;
			if(dis[v]>edge[u][i].w) {
				dis[v]=edge[u][i].w;
				q.push({v,dis[v]});
			}
		}
	}
	if(cnt==n) printf("%d\n",ans);
	else printf("-1\n");
	return ;
}
int main() {
	n=read();m=read();
	for(int i=1;i<=m;i++) {
		int u=read(),v=read(),w=read();
		edge[u].push_back({v,w});
		edge[v].push_back({u,w});
	}
	prim(1);
	return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Cocos Creator 3.61所有工具软件的使用

文章目录Tiled-地图绘制软件下载安装基本使用点点就会了导入cocos编辑动画注意cocos导入TiledBigShear-图片裁剪为plist形式下载安装使用TexturePacker-使用plist文件产生精灵图集下载安装Tiled-地图绘制软件 下载安装 官网免费 Tiled官方 安装无要求 基本使用 点点就会了 导…

UE5 Meerkat狐獴演示Demo分析

1.特效的生成方式 1.1临时特效的生成&#xff1a;使用了已生成轨道临时创建该特效&#xff08;不用在场景中放入该特效&#xff0c;而是临时创建即可&#xff09;、系统生命周期轨道设置该特效的播放时长 1.2长期特效的生成&#xff1a;特效时长为该镜头片段长度 2.特效的类…

软考高级哪个好考?

软考高级有5个科目。 开发方向的有系统分析师&#xff0c;系统架构师&#xff1b; 网络方向的有网络规划与设计师&#xff1b; 信息系统方向的有信息系统项目管理师&#xff1b; 信息化服务方向的有系统规划与管理师。 考试题型 上午是9:00 — 11:30考综合知识&#xff0c;…

ROBOGUIDE软件:FANUC机器人多层堆焊功能介绍与示教编程操作方法

目录 机器人多层堆焊功能介绍 机器人跟踪路径数据指令介绍 机器人多层堆焊指令介绍 机器人弧焊焊接工作站创建 机器人多层堆焊示教编程 仿真运行 机器人多层堆焊功能介绍 在厚板焊接中进行多层堆焊焊接&#xff0c;以便多次焊接相同的部位而增大焊接宽度。通常情况下&am…

Java池化技术

在我们平常的编码中&#xff0c;通常会将一些对象保存起来&#xff0c;这主要考虑的是对象的创建成本。比如像线程资源、数据库连接资源或者 TCP 连接等&#xff0c;这类对象的初始化通常要花费比较长的时间&#xff0c;如果频繁地申请和销毁&#xff0c;就会耗费大量的系统资源…

Java-String 类·上

Java-String 类上1. 创建字符串2. 字符串比较相等3. 字符串常量池4. 理解字符串不可变大家好&#xff0c;我是晓星航。今天为大家带来的是Java String字符串相关知识点的讲解&#xff01;&#x1f600; 1. 创建字符串 常见的构造 String 的方式 // 方式一 String str "…

轻量化网络ShuffleNet 旷视

CVPR2018 人脸识别 脸部特效 张翔宇 什么是分组卷积 我们可以回忆一下 普通卷积 feature map有几个 我们的对应的卷积核就需要几个channel 然后我们学习这个 分组卷积 如图所示&#xff0c;前两个channel 有一个2个channel的卷积核负责&#xff0c;两个与两个对应 来自这…

学习.NET MAUI Blazor(四)、路由

Web应用程序的可以通过URL将多个页面串联起来&#xff0c;并且可以互相跳转。Web应用主要是使用a标签或者是服务端redirect来跳转。而现在流行的单页应用程序 (SPA) &#xff0c;则通过路由&#xff08;Router&#xff09;来实现跳转&#xff0c;如Vue 、React等。 提示 MAUI的…

C#一个网络小程序的逐步实现过程

经常要检测某些IP地址范围段的计算机是否在线。 有很多的方法&#xff0c;比如进入到网关的交换机上去查询、使用现成的工具或者编写一个简单的DOS脚本等等&#xff0c;这些都比较容易实现。 现在使用C#来完成。 1、简单上手 公用函数&#xff1a; public static long IPToLong…

传奇服务器容易受到什么攻击,怎么防御攻击?

有兄弟问明杰&#xff0c;说自己打算开服&#xff0c;听说攻击挺多的&#xff0c;就是想先了解一下开传奇用的服务器最容易受到什么类型的攻击&#xff0c;如果遇到了又改怎么防御呢&#xff1f;带着这个问题&#xff0c;明杰跟大家详细的说一下&#xff0c;常见的开区时候遇到…

Max Sum Plus Plus(DP 滚动数组优化)[HDU - 1024]

题目如下&#xff1a; 题目链接 Max Sum Plus Plus 题解 or 思路&#xff1a; 经典的动态规划问题 dp[i][j]dp[i][j]dp[i][j], 前 jjj 个物品&#xff0c; 我们分成题目要求的 iii 组 对于第 jjj 个物品&#xff0c; 我们可以将它分到 第 kkk 组中&#xff0c; 或者分到新的一…

前端八股文——笔试题

目录 前言 一、flex布局手写题目 二、移动端点击事件为什么有延迟&#xff1f;时间多久&#xff1f;如何解决这个问题&#xff1f; 1.meta标签里面content属性&#xff0c;设置禁止缩放。 2.设置默认宽度为浏览器宽度。 3.设置touch-action&#xff1a;manipulation。 4…

Chat-GPT从注册到搬进服务器

首先&#xff0c;我们要明白的一个事实是&#xff0c;不可能免费的成功搞定&#xff0c;得付出10几块得成本&#xff0c;我这个方法满打满算16块钱&#xff08;是不犯错得情况下&#xff0c;实际上我用了30多了&#xff09; 1.10元买个香港的服务器 不一定是香港的&#xff0c…

Day 04-Composition API_ref reactive 函数

1.ref函数 作用: 定义一个响应式的数据&#xff1b; 语法: const xxx ref(initValue) 创建一个包含响应式数据的引用对象&#xff08;reference对象&#xff0c;简称ref对象&#xff09;。 JS中操作数据&#xff1a; xxx.value 模板中读取数据: 不需要.value&#xff0c;直…

永恒之蓝(MS17-010)

目录追溯了解网络IP查找环境条件复现流程445端口使用MSF的永恒之蓝漏洞模块扫描模块攻击模块温馨提醒&#xff1a;纯水文&#xff0c;如果不幸翻到这篇文章&#xff0c;可以立刻关闭&#xff01; 先整理两个学习的链接&#xff08;本文学习第一个&#xff09;&#xff1a; htt…

JDK19都出来了~是时候梳理清楚JDK的各个版本的特性了【JDK17特性讲解】

JDK各个版本特性讲解-JDK17特性 一、JAVA17概述 JDK 16 刚发布半年&#xff08;2021/03/16&#xff09;&#xff0c;JDK 17 又如期而至&#xff08;2021/09/14&#xff09;&#xff0c;这个时间点特殊&#xff0c;蹭苹果发布会的热度&#xff1f;记得当年 JDK 15 的发布也是同天…

Kafka Consumer auto.offset.reset 理解

先来一下 kafka 官网对于 auto.offset.reset 的解释&#xff1a; 上面的描述挺准确的&#xff0c;但如果没有相关背景会感觉很懵逼。网上也有很多文章讲这个东西并给了很多例子&#xff0c;看了之后总感觉没有理解清楚。 先来看一下怎么查看消费者 group 的 offset 情况&…

wpf需求及实现方法 动态创建控件 对数据模板任意对象操作 查找由 DataTemplate 生成的元素 查找由 ControlTemplate 生成的元素

我想实现一个支持多设备同时更新固件的应用。如下图 插入多少个设备就显示多少个进度度。每个进度条上显示对应的串口及进度。 最终实现演示如下&#xff1a; public MainWindow(){InitializeComponent();List<DownloadProgress> test new List<DownloadProgress>…

文件系统与文件系统管理以及RAID技术的思想

文件与文件系统FCB&#xff08;文件控制块&#xff09;文件是什么&#xff1f;文件是对 磁盘的抽象所谓文件 是指 一组带标识&#xff08;标识即为文件名&#xff09;的、在逻辑上有完整意义的信息项的序列。信息项&#xff1a;构成文件内容的基本单位&#xff08;单个…

AUTOSAR开发

综述 本文档主要描述了VP项目MCU芯片TC297的AUTOSAR方案。MCU的基础软件由AUTOSAR软件实现&#xff0c;AUTOSAR可简易理解为如下层次。 MCU芯片驱动层&#xff1a;MCU芯片的抽象层&#xff0c;目的是将各类MCU芯片进行抽象&#xff0c;向上统一接口&#xff0c;隔离其他层次软件…