【代码随想录训练营第42期 Day56打卡 - 图论Part6 - 并查集2 - 冗余连接问题

news2024/9/24 11:07:53

目录

一、做题心得

二、题目与题解

题目一:108. 冗余连接

题目链接

题解:并查集

题目二:109. 冗余连接II

题目链接

题解:并查集

三、小结


一、做题心得

冗杂连接问题是图论章节应用并查集的经典问题。所有的顶点通过边相连,且除了一个多余的边之外,其他的边都是必须的,用以保持图的所有顶点连通,而我们需要做的,就是删除这条边,即冗杂的边。

这里打卡的两道题分别从无向边和有向边两种情况进行考虑。

二、题目与题解

题目一:108. 冗余连接

题目链接

108. 冗余连接 (kamacoder.com)

题目描述

有一个图,它是一棵树,他是拥有 n 个节点(节点编号1到n)和 n - 1 条边的连通无环无向图(其实就是一个线形图),如图:

现在在这棵树上的基础上,添加一条边(依然是n个节点,但有n条边),使这个图变成了有环图,如图:

先请你找出冗余边,删除后,使该图可以重新变成一棵树。

输入描述

第一行包含一个整数 N,表示图的节点个数和边的个数。

后续 N 行,每行包含两个整数 s 和 t,表示图中 s 和 t 之间有一条边。

输出描述

输出一条可以删除的边。如果有多个答案,请删除标准输入中最后出现的那条边。

输入示例

3
1 2
2 3
1 3

输出示例

1 3

提示信息

图中的 1 2,2 3,1 3 等三条边在删除后都能使原图变为一棵合法的树。但是 1 3 由于是标准输出里最后出现的那条边,所以输出结果为 1 3

数据范围

1 <= N <= 1000.

题解:并查集

这题就是昨天打卡模板的经典应用。

这里需要注意的是:

判断是否形成环:在将这对节点合并之前,会调用 isSame 函数来判断 s 和 t 是否已经在同一个集合中

如果 isSame(s, t) 返回 true,则说明 s 和 t 已经在同一个集合中,合并它们将会形成一个环。因此,程序会输出这对节点并结束

合并集合:如果 isSame 函数返回 false,说明 s 和 t 不在同一个集合中,合并它们不会形成环,则调用 join 函数将 s 和 t 所在的集合合并。

代码如下:

#include <bits/stdc++.h>
using namespace std;

int n;
vector<int> father(101, 0);

// 并查集初始化
void init()
{
    for (int i = 1; i <= n; i++) // 节点是从1到n
        father[i] = i;           
}

// 并查集里寻找该节点的根节点(带路径压缩)
int find(int u)
{
    if (u != father[u])
        father[u] = find(father[u]);
    return father[u];
}

// 判断u和v是否找到同一个根节点 -- 是否在同一个集合中
bool isSame(int u, int v)
{
    int rootu = find(u);
    int rootv = find(v);
    return rootu == rootv;
}

// join 函数用于合并两个节点所在的集合:将v->u这条边加入并查集
void join(int u, int v)
{
    int rootu = find(u);
    int rootv = find(v);
    if (rootu != rootv)
        father[rootv] = rootu;
}

int main()
{
    cin >> n;
    init(); // 初始化并查集
    while (n--)
    {
        int s, t;
        cin >> s >> t;
        if (isSame(s, t))
        {
            cout << s << " " << t;
            return 0;
        }
        else
            join(s, t);
    }
}

题目二:109. 冗余连接II

题目链接

109. 冗余连接II (kamacoder.com)

题目描述

有一种有向树,该树只有一个根节点,所有其他节点都是该根节点的后继。该树除了根节点之外的每一个节点都有且只有一个父节点,而根节点没有父节点。有向树拥有 n 个节点和 n - 1 条边。如图: 

现在有一个有向图,有向图是在有向树中的两个没有直接链接的节点中间添加一条有向边。如图:

输入一个有向图,该图由一个有着 n 个节点(节点编号 从 1 到 n),n 条边,请返回一条可以删除的边,使得删除该条边之后该有向图可以被当作一颗有向树。

输入描述

第一行输入一个整数 N,表示有向图中节点和边的个数。 

后续 N 行,每行输入两个整数 s 和 t,代表这是 s 节点连接并指向 t 节点的单向边

输出描述

输出一条可以删除的边,若有多条边可以删除,请输出标准输入中最后出现的一条边。

输入示例

3
1 2
1 3
2 3

输出示例

2 3

提示信息

在删除 2 3 后有向图可以变为一棵合法的有向树,所以输出 2 3

数据范围:

1 <= N <= 1000.

题解:并查集

注意这题变成了有向图。

一个关键

在有向图中,如果一个节点的入度为2,那么它至少是环的一部分。这是因为至少存在两条边从不同的节点指向该节点,如果这些边之间没有其他的边连接,那么它们就构成了一个环 。

然后需要注意的是,对于有向图问题,我们需要用一个二维数组来存储所有有向边(该题中使用edges[][] 存储,edges[s][t]表示从s到t的有向边)

这里直接看代码:

#include <bits/stdc++.h>
using namespace std;

int n;
vector<int> father(1001, 0);

// 并查集初始化
void init()
{
    for (int i = 1; i <= n; i++) // 节点是从1到n
        father[i] = i;
}

// 并查集里寻找该节点的根节点(带路径压缩)
int find(int u)
{
    if (u != father[u])
        father[u] = find(father[u]);
    return father[u];
}

// 判断u和v是否找到同一个根节点 -- 是否在同一个集合中
bool isSame(int u, int v)
{
    int rootu = find(u);
    int rootv = find(v);
    return rootu == rootv;
}

// join 函数用于合并两个节点所在的集合:将v->u这条边加入并查集
void join(int u, int v)
{
    int rootu = find(u);
    int rootv = find(v);
    if (rootu != rootv)
        father[rootv] = rootu;
}

// 在有向图里找到删除的那条边,使其变成树
void getRemoveEdge(const vector<vector<int>> &edges)
{
    init();                     // 初始化并查集
    for (int i = 0; i < n; i++) // 遍历所有的边
    {
        if (isSame(edges[i][0], edges[i][1])) // 如果构成有向环,则删除这条边
        {
            cout << edges[i][0] << " " << edges[i][1];
            return;
        }
        else
            join(edges[i][0], edges[i][1]); // 否则将边加入并查集
    }
}

// 删一条边之后判断是不是树
bool isTreeAfterRemoveEdge(const vector<vector<int>> &edges, int deleteEdge)
{
    init(); // 初始化并查集
    for (int i = 0; i < n; i++)
    {
        if (i == deleteEdge)
            continue; // 跳过需要删除的边
        if (isSame(edges[i][0], edges[i][1]))
            return false; // 如果构成有向环,则不是树
        else
            join(edges[i][0], edges[i][1]); // 将边加入并查集
    }
    return true; // 如果没有构成环,则是树
}

int main()
{
    int s, t;
    vector<vector<int>> edges;      // 存储所有边 -- 有向:edge[s][t]表示边 s->t
    cin >> n;                       // 输入边的数量
    vector<int> inDegree(n + 1, 0); // 记录节点入度
    for (int i = 0; i < n; i++)
    {
        cin >> s >> t;           // 输入边s->t(有向边)
        inDegree[t]++;           // 增加节点t的入度
        edges.push_back({s, t}); // 将边加入边列表 edge
    }

    vector<int> vec; // 存储入度为2的边的索引
    // 注意:找到入度为2的节点所对应的边,倒序遍历以优先删除最后出现的边
    for (int i = n - 1; i >= 0; i--)
    {
        if (inDegree[edges[i][1]] == 2)
            vec.push_back(i); // 将边的索引加入vec
    }
    // 如果有入度为2的边,则尝试删除这些边
    if (vec.size() > 0)
    {
        // 优先删除vec[0]这条边
        if (isTreeAfterRemoveEdge(edges, vec[0]))
            cout << edges[vec[0]][0] << " " << edges[vec[0]][1];
        else
            cout << edges[vec[1]][0] << " " << edges[vec[1]][1];
        return 0;
    }

    // 如果没有入度为2的边,则一定有有向环,找到构成环的边并删除
    getRemoveEdge(edges);
}

三、小结

今天的打卡到此结束,图论的题很繁琐,需要花时间总结消化,后边会试着多练习这一块的内容。最后,希望终有所获。

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

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

相关文章

Redis 篇- 实战项目中使用 Redis 实现经典功能(异步秒杀商品、点赞功能、共同关注的好友、投喂功能)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 使用 Redis 实现异步秒杀 1.1 基于 Lua 脚本判断是否符合条件&#xff1a;库存是否充足、一人一单 1.2 基于 Redis 中的 Stream 实现消息队列 1.3 使用 Java 操作…

Unity Hub自动安装指定版本Unity的Android开发环境

Unity开发Android环境要求SDK、DNK、JDK、Gradle版本都要对才能发布APK&#xff0c;自己去配置很容易出错。Unity Hub可以自动安装指定版本Unity的Android开发环境。 1.安装国内用的UnityHub&#xff08;我这里用的3.3.2-c6&#xff09; 2.找到对应的Unity版本 3.点击【从Unit…

docker管理redis集群

1.拉取redis镜像 docker pull redis拉取完成 [rootlocalhost ~]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE redis latest a617c1c92774 3 years ago 105MB2.运行redis容器 docker run -itd --name redis-test01 -p 6379:6379…

谈谈PCIe VID、DID、SSID、SSVID背后的智慧

PCIe Vendor ID 想了半天还是觉得从“ID是什么”这个问题开始比较好。那么ID是什么&#xff1f;ID就是身份。那身份又是什么&#xff1f;身份就是一个合理存在&#xff0c;用于区分不同个体。为什么叫“合理存在”呢&#xff1f;如果国家不给你发身份证&#xff0c;你就是黑户…

记一次导入dbf文件后数据为空问题的解决方法

前言 省流&#xff1a;这篇文章最终采用的是更换导出文件格式的方法&#xff0c;看到这里觉得方法不适用的小伙伴可以不用浪费几秒钟看完这篇文章哦。 问题描述 作者使用的是Navicat数据库管理工具&#xff0c;然后在将源数据库的数据表导出为dbf格式文件后&#xff0c;再将…

F110批量付款如何Debug BTE增强(后台JOB的调试方法)

F110批量付款如何Debug BTE增强&#xff08;后台JOB的调试方法&#xff09; SAP系统中的F110&#xff08;Automatic Payment&#xff09;是一个常用的付款程序&#xff0c;在实施过程中&#xff0c;也经常会遇到一些运行的错误&#xff0c;而对于此类的错误&#xff0c;通常的…

基于SpringBoot的租房网站系统

你好呀&#xff0c;我是计算机学姐码农小野&#xff01;如果有相关需求&#xff0c;可以私信联系我。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot框架 工具&#xff1a;ECLIPSE 系统展示 首页 管理员功能界面 用户信息界面 预约看房界…

动手学深度学习(pytorch)学习记录28-使用块的网络(VGG)[学习记录]

目录 VGG块VGG网络训练模型 VGG块 定义了一个名为vgg_block的函数来实现一个VGG块 import torch from torch import nn from d2l import torch as d2ldef vgg_block(num_convs, in_channels, out_channels):layers []for _ in range(num_convs):layers.append(nn.Conv2d(in_…

线程池原理及改造

目录 一 线程池执行原理 二 线程池改造(一) 三 线程池改造(二) 一 线程池执行原理 首先我们先了解一下线程池里面几个参数&#xff1a; 第一个是核心线程数&#xff0c;第二个是线程池最大线程数。&#xff08;线程池里面的线程分为核心线程和非核心线程&#xff0c;既然核心…

人物化身持有者每月奖励:九月版

世界急需英雄。你准备好响应号召了吗&#xff1f; 穿上你的斗篷&#xff0c;戴上你的面具&#xff0c;用你的风格保卫 The Sandbox 的街道吧&#xff01;本月为人物化身持有者准备的独家奖励是 The Sandbox 超级套装&#xff01; 本月我们将首次向我们生态系统中的所有人物化…

Vulnhub-RickdiculouslyEasy靶机攻略

御剑扫描到ip 一.第一个flag 主机扫描 目录扫描 二.网页信息收集-第二个flag 9090也开放了web服务所以我们在IP地址后面加端口试试&#xff0c;如下图&#xff0c;加上了端口&#xff0c;并且发现了第二个flag&#xff0c;也对第二个flag进行了简单的探索也没有发现什么可以…

Kubernetes上安装Metallb和Ingress并部署应用程序

视频和代码仓库 视频教程地址&#xff1a;https://www.bilibili.com/video/BV1QV4rebEb8 代码仓库地址&#xff1a;https://github.com/xiaohh-me/kubernetes-yaml 网络规划 之前已经写了几篇安装Kubernetes文章&#xff0c;这次来讲讲在Kubernetes上安装Ingress&#xff0c…

【Linux】Linux介绍及CentOS虚拟机环境搭建

内容大纲介绍 文章目录 内容大纲介绍1.计算机简介2.Linux系统介绍3.虚拟化软件介绍4.Linux环境搭建5.扩展_虚拟机的快照6.Linux的目录介绍 1.计算机简介 概述 全称叫电子计算机, 英文名叫Computer, 俗称叫: 电脑, 简称叫: PC, 就是有硬件和软件组成的电子设备. 组成 计算机硬件…

IO复用-epoll基础

文章目录 IO复用认识epollepoll原理重要结构体 epoll的ET、LTepoll高效的原因epoll需要解决的问题对于使用epoll的建议 IO复用 IO等待拷贝 IO复用是一种通过减少等待时间&#xff0c;来提高IO效率的方式。 其原理是通过同时管理多个IO接口&#xff08;文件描述符&#xff09;…

VS2022搭建Linux开发环境

一、VS2022按钮Linux开发组件 双击启动后 选择Linux开发组件 点击修改&#xff0c;开始按钮Linux组件 二、创建新项目 三、远程连接Linux系统 选择工具&#xff0c;点击选项 选择跨平台中的连接管理器&#xff0c;点击添加 配置信息&#xff0c;输入你需要连接到的Linux…

《论面向服务架构设计及其应用》写作框架,软考高级系统架构设计师

论文真题 面向服务架构(Service-Oriented Architecture, SOA) 是一种应用框架,将日常的业务应用划分为单独的业务功能服务和流程,通过采用良好定义的接口和标准协议将这些服务关联起来。通过实施基于SOA的系统架构,用户可以构建、部署和整合服务,无需依赖应用程序及其运…

PyCharm的安装步骤

如何在本机上下载和安装PyCharm&#xff0c;请看以下讲解&#xff1b; 目录 一、下载 二、安装 第 1 步&#xff1a; 第 2 步&#xff1a; 第 3 步&#xff1a; 第 4 步&#xff1a; 第 5 步&#xff1a; 三、配置 第 1 步&#xff1a; 第 2 步&#xff1a; 第 3 步…

基于SpringBoot+Vue+MySQL的实训管理系统

系统展示 用户前台界面 管理员后台界面 系统背景 在当今信息化高速发展的时代&#xff0c;实训管理系统的建设对于提升教育机构的教学效率、优化资源配置、增强师生互动具有重要意义。本系统基于SpringBoot框架构建后端服务&#xff0c;利用其高效、简洁的特点&#xff0c;快速…

U盘文件及文件夹带锁修复

磁盘管理修复工具Disks磁盘管理–针对U盘文件及文件夹带锁修复 本文章只针对统信系统 文章目录 功能概述一、安装工具二、数据备份三、检查文件系统1. 通过启动栏中的“磁盘”或者桌面的“磁盘”启动文件来启动应用:2. 选择U盘设备3. 点击“检查文件系统”按钮(如果无此按钮…

【计算机毕设-大数据方向】基于大数据的健康美食推荐系统设计与实现

&#x1f497;博主介绍&#xff1a;✌全平台粉丝5W,高级大厂开发程序员&#x1f603;&#xff0c;博客之星、掘金/知乎/华为云/阿里云等平台优质作者。 【源码获取】关注并且私信我 【联系方式】&#x1f447;&#x1f447;&#x1f447;最下边&#x1f447;&#x1f447;&…