训练题解_P1536 村村通
关键词
村村通工程、城镇连通性、并查集、最少道路建设
思路
本题的核心是计算在给定现有道路的情况下,为了使所有城镇都相互连通,最少还需要建设多少条道路。可以将每个城镇看作一个节点,每条道路看作一条边,那么问题就转化为在一个图中,计算需要添加多少条边才能使图成为一个连通图。我们可以使用并查集来解决这个问题,通过并查集可以快速判断两个城镇是否已经连通,并将连通的城镇合并到同一个集合中。最后,统计集合的数量,集合的数量减 1 就是最少还需要建设的道路数目。
步骤
- 初始化并查集:对于每个城镇,将其初始化为一个独立的集合,即每个城镇的父节点是它自身。
- 处理现有道路:对于每一条现有道路,将道路连接的两个城镇所在的集合合并。如果两个城镇已经在同一个集合中,则不需要进行合并操作。
- 统计集合数量:遍历所有城镇,统计不同集合的数量。可以通过查找每个城镇的根节点,将根节点不同的集合看作不同的集合。
- 计算最少道路数目:最少还需要建设的道路数目等于集合的数量减 1。因为要使所有城镇连通,只需要将这些不连通的集合连接起来即可,而连接 \(k\) 个不连通的集合最少需要 \(k - 1\) 条边。
- 处理多组测试数据:由于输入包含若干组测试数据,需要循环读取每组数据,直到遇到输入为 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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】