训练题解_P1536 村村通

关键词

村村通工程、城镇连通性、并查集、最少道路建设

思路

本题的核心是计算在给定现有道路的情况下,为了使所有城镇都相互连通,最少还需要建设多少条道路。可以将每个城镇看作一个节点,每条道路看作一条边,那么问题就转化为在一个图中,计算需要添加多少条边才能使图成为一个连通图。我们可以使用并查集来解决这个问题,通过并查集可以快速判断两个城镇是否已经连通,并将连通的城镇合并到同一个集合中。最后,统计集合的数量,集合的数量减 1 就是最少还需要建设的道路数目。

步骤

  1. 初始化并查集:对于每个城镇,将其初始化为一个独立的集合,即每个城镇的父节点是它自身。
  2. 处理现有道路:对于每一条现有道路,将道路连接的两个城镇所在的集合合并。如果两个城镇已经在同一个集合中,则不需要进行合并操作。
  3. 统计集合数量:遍历所有城镇,统计不同集合的数量。可以通过查找每个城镇的根节点,将根节点不同的集合看作不同的集合。
  4. 计算最少道路数目:最少还需要建设的道路数目等于集合的数量减 1。因为要使所有城镇连通,只需要将这些不连通的集合连接起来即可,而连接 \(k\) 个不连通的集合最少需要 \(k - 1\) 条边。
  5. 处理多组测试数据:由于输入包含若干组测试数据,需要循环读取每组数据,直到遇到输入为 0 表示测试数据结束。对于每组数据,重复上述步骤 1 - 4 进行计算,并输出结果。

CPP CODE

#include <cstdio>
// 自定义输入缓冲区
const int INPUT_BUFFER_SIZE = 1 << 20;
char inputBuffer[INPUT_BUFFER_SIZE];
char* inputPtr = inputBuffer;
char* inputEnd = inputBuffer;
// 填充输入缓冲区
inline void fillInputBuffer() {
inputEnd = inputBuffer + fread(inputBuffer, 1, INPUT_BUFFER_SIZE, stdin);
inputPtr = inputBuffer;
}
// 跳过空白字符
inline void skipWhitespace() {
while (inputPtr < inputEnd && (*inputPtr <= ' ')) ++inputPtr;
if (inputPtr >= inputEnd) fillInputBuffer();
while (inputPtr < inputEnd && (*inputPtr <= ' ')) ++inputPtr;
}
// 快速读取整数
inline int readInt() {
skipWhitespace();
int num = 0;
do {
num = num * 10 + (*inputPtr++ - '0');
if (inputPtr >= inputEnd) fillInputBuffer();
} while (*inputPtr >= '0' && *inputPtr <= '9');
return num;
}
// 自定义输出缓冲区
const int OUTPUT_BUFFER_SIZE = 1 << 20;
char outputBuffer[OUTPUT_BUFFER_SIZE];
char* outputPtr = outputBuffer;
// 刷新输出缓冲区
inline void flushOutputBuffer() {
fwrite(outputBuffer, 1, outputPtr - outputBuffer, stdout);
outputPtr = outputBuffer;
}
// 快速写入整数
inline void writeInt(int num) {
static char temp[20];
char* p = temp;
do {
*p++ = num % 10 + '0';
num /= 10;
} while (num);
while (p != temp) {
*outputPtr++ = *--p;
if (outputPtr >= outputBuffer + OUTPUT_BUFFER_SIZE - 10) {
flushOutputBuffer();
}
}
}
// 快速写入换行符
inline void writeNewLine() {
*outputPtr++ = '\n';
if (outputPtr >= outputBuffer + OUTPUT_BUFFER_SIZE - 10) {
flushOutputBuffer();
}
}
const int MAXN = 1000001;
int fa[MAXN], rank[MAXN];
// 内联迭代路径压缩查找
inline int find(int x) {
int root = x;
while (root != fa[root]) root = fa[root];
while (x != root) {
int next = fa[x];
fa[x] = root;
x = next;
}
return root;
}
// 内联按秩合并
inline void unite(int x, int y, int& componentCount) {
int rootX = find(x);
int rootY = find(y);
if (rootX == rootY) return;
if (rank[rootX] < rank[rootY]) {
fa[rootX] = rootY;
} else {
fa[rootY] = rootX;
if (rank[rootX] == rank[rootY]) ++rank[rootX];
}
--componentCount;
}
int main() {
int n, m, x, y;
while ((n = readInt()) != 0) {
m = readInt();
for (int i = 1; i <= n; ++i) {
fa[i] = i;
rank[i] = 0;
}
int componentCount = n;
for (int i = 0; i < m; ++i) {
x = readInt();
y = readInt();
unite(x, y, componentCount);
}
writeInt(componentCount - 1);
writeNewLine();
}
flushOutputBuffer();
return 0;
}
posted @   半眠七点不想睡  阅读(3)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
/* 设置动态特效 */
点击右上角即可分享
微信分享提示