热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

PinelyRound1(Div.1+Div.2)-连通图构建问题(逻辑分析/并查集+分类讨论)

本题探讨如何通过特定操作使给定的无向图完全连通,涉及并查集及多种情况下的策略分析。

问题描述

给定一个包含最多4000个节点的无向图,图中不存在自环或多重边。

允许进行的操作包括:选择任一节点u,对于除u外的每一个节点v,如果u和v当前已直接相连,则断开该边;反之,则添加一条连接u和v的边。

目标是计算最少需要执行上述操作多少次才能使整个图变为连通状态,并指出需要操作的具体节点编号。

注意,测试案例总数不超过800组,所有案例中节点总数之和不超过4000。


解题思路

本题解基于官方题解和社区贡献代码整理而成。


解决方案

针对不同情况,采取相应的策略:

1. 如果图本身就是一个连通块,无需任何操作,答案为0。

2. 存在一个孤立节点时,只需将其与其他所有节点连接起来,答案为1,输出该孤立节点的编号。

3. 当存在至少一个非完全连通的子图时,可以通过操作该子图中度数最小的节点x,使其与原子图保持连通的同时,与其它子图建立连接,答案同样为1,输出节点x的编号。

4. 若图由两个完全连通的子图组成,需将较小的子图完全拆散,以便与较大的子图合并,答案等于较小子图中的节点数,输出这些节点的编号。

5. 对于三个或更多完全连通的子图,先从任意一个子图中选取一点进行操作,再从另一个子图中选择另一点操作,确保所有子图最终实现连通,答案固定为2,输出两次操作所选的节点编号。


技巧分享

对于第三种情况,关键在于理解为何选择度数最小的节点x进行操作可以保证图的连通性。这涉及到一个重要的引理:在一个非完全连通的子图中,度数最小的节点x,通过反转其所有邻接关系后,图仍保持连通。此引理可通过分析x是否为割点及其与其它节点的连接特性来证明。


代码实现

以下是C++语言的实现代码,用于解决上述问题:

#include
using namespace std;
typedef long long ll;
const int maxn = 4005;
int t, n, u, v, par[maxn], sz[maxn], deg[maxn];
char s[maxn];
int find(int x) { return par[x] == x ? x : par[x] = find(par[x]); }
void solve() {
int ans = 0, now = n, pos = -1;
for (int i = 0; i if (find(i) == i) {
ans++;
if (now > sz[i]) {
now = sz[i];
pos = i;
}
}
}
if (ans == 1) {
puts("0");
return;
}
for (int i = 0; i int f = find(i);
if (f == i && sz[i] == 1) {
puts("1");
printf("%d\n", i + 1);
return;
}
if (sz[f] - 1 != deg[i]) {
int mx = n, id = -1;
for (int j = 0; j if (find(j) == f) {
if (mx > deg[j]) {
mx = deg[j];
id = j;
}
}
}
puts("1");
printf("%d\n", id + 1);
return;
}
}
if (ans == 2) {
printf("%d\n", sz[pos]);
for (int i = 0; i if (find(i) == pos) {
printf("%d ", i + 1);
}
}
puts("");
return;
} else {
int cnt = 0;
puts("2");
for (int i = 0; i if (find(i) == i) {
printf("%d ", i + 1);
cnt++;
if (cnt == 2) {
puts("");
return;
}
}
}
}
}
int main() {
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
for (int j = 0; j par[j] = j;
sz[j] = 1;
deg[j] = 0;
}
for (int j = 0; j scanf("%s", s);
for (int k = 0; k if (s[k] == '1') {
deg[k]++;
deg[j]++;
int a = find(j), b = find(k);
if (a == b) continue;
sz[a] += sz[b];
par[b] = a;
}
}
}
solve();
}
return 0;
}

推荐阅读
  • 本文详细介绍了如何通过修改Lua源码或使用动态链接库(DLL)的方式实现Lua与C++之间的高级交互,包括如何编译Lua源码、添加自定义API以及在C++中加载和调用Lua脚本。 ... [详细]
  • 本次竞赛包含三个编程题目,旨在考察参赛者对数学逻辑及时间处理的能力。题目涉及筛选特定条件下的数字、Unix时间戳转换以及数列中元素关系的分析。 ... [详细]
  • 【UOJ】#37. 【清华集训2014】主旋律
    题解一道,神奇的题我们考虑正难则反,我们求去掉这些边后有多少图不是强连通的怎么求呢,不是强连通的图缩点后一定是一个DAG,并 ... [详细]
  • 本文介绍了如何在C++中使用new关键字动态创建一维和二维数组,并详细解释了常见的错误及其解决方案。 ... [详细]
  • socket函数SOCKET()我们使用系统调用socket()来获得文件描述符:#include#includei ... [详细]
  • 本文介绍了一种使用状态压缩动态规划(状压DP)解决售货员难题的方法。通过定义dp[S][i]表示状态S下以i作为终点的最小代价,详细解释了状态转移方程及其实现。 ... [详细]
  • nsitionalENhttp:www.w3.orgTRxhtml1DTDxhtml1-transitional.dtd ... [详细]
  • 本文介绍如何利用QFileSystemModel进行目录的浏览、创建及删除操作,并提供了一个简单的对话框界面实现。 ... [详细]
  • 本文介绍了两种实现UTF-8与Unicode编码之间相互转换的方法:一种是不使用Visual C++库函数的手动转换方法,另一种则是利用Visual C++提供的库函数进行高效转换。 ... [详细]
  • 题目链接:https://www.acwing.com/problem/content/3662/。此题涉及一辆汽车从起点S出发,前往终点E,途中需经过多个加油站。要求计算汽车在确保能顺利抵达终点的前提下,最少需要在哪些加油站加油。 ... [详细]
  • 寒武纪C++实习面试经验分享
    本文详细介绍了C++中的一些关键知识点,包括继承方式、虚继承、多态性以及引用与指针的使用场景。通过具体实例和代码示例,帮助读者更好地理解和应用这些概念。 ... [详细]
  • 本文介绍了如何使用Objective-C语言遍历指定文件夹,并根据文件扩展名来判断文件类型的方法。代码示例中通过创建一个文件管理器实例,利用目录枚举器遍历文件夹中的所有项,筛选出特定类型的文件。 ... [详细]
  • 本文介绍两种在Qt中有效解决中文乱码的方法,包括通过设置编码方式和直接使用UTF-8字符集。 ... [详细]
  • 应用场景在开发中,我们经常需要把一些随时可能变化的属性配置到配置文件中,这样耦合性低,方便维护。SpringBoot在这方面为我们提供了很大的便捷,我们可以很轻易的将propert ... [详细]
  • 本文介绍了几个使用C++语言实现的递归算法案例,包括计算数组和、数组倒置、打印数字三角形以及解决经典的汉诺塔问题。 ... [详细]
author-avatar
泰坦尼雅牧民
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有