比赛链接
c题目描述
给定一个具有 N 个顶点和 M 条边的简单无向图,顶点编号从 1 到 N,第 i 条边连接顶点 (u_i) 和 (v_i)。需要计算从该图中删除最少多少条边,才能使图变成一个森林(即不包含任何环的简单无向图)。
代码思路
本题使用并查集(Disjoint Set Union,DSU)来解决。并查集是一种用于处理不相交集合的合并与查询问题的数据结构,非常适合用于判断图中是否存在环。具体思路如下:
初始化并查集:为每个顶点创建一个独立的集合,每个集合的父节点初始化为自身。
遍历每条边:对于每一条边 ((u, v)),检查 u 和 v 是否属于同一个集合。
- 如果 u 和 v 不属于同一个集合,说明这条边不会形成环,将这两个集合合并。
- 如果 u 和 v 属于同一个集合,说明这条边会形成环,需要将其删除,记录删除边的数量。
输出结果:遍历完所有边后,输出需要删除的边的数量。代码实现
#include <iostream>
#include <vector>
using namespace std;// 查找元素 x 所在集合的根节点,并进行路径压缩
int find(int x, vector<int>& parent) {if (parent[x] != x) {parent[x] = find(parent[x], parent); // 路径压缩}return parent[x];
}// 合并元素 x 和 y 所在的集合
void unionSet(int x, int y, vector<int>& parent, vector<int>& rank) {int rootX = find(x, parent);int rootY = find(y, parent);if (rootX != rootY) {if (rank[rootX] > rank[rootY]) {parent[rootY] = rootX;} else if (rank[rootX] < rank[rootY]) {parent[rootX] = rootY;} else {parent[rootY] = rootX;rank[rootX]++;}}
}int main() {int N, M;cin >> N >> M;vector<int> parent(N + 1), rank(N + 1, 0);for (int i = 1; i <= N; i++) {parent[i] = i; // 初始化并查集}int deleteCount = 0;for (int i = 0; i < M; i++) {int u, v;cin >> u >> v;if (find(u, parent) != find(v, parent)) {unionSet(u, v, parent, rank);} else {deleteCount++; // 如果已连接,说明是环,记录删除}}cout << deleteCount << endl;return 0;
}
d问题分析
我们需要找到满足以下条件的整数对 (a, b) 的数量:
a 和 b 的两个出现位置在原数组中均不相邻。
通过交换 a 和 b 的位置,可以使 a 和 b 的两个出现位置均相邻。
关键观察:当且仅当 a 和 b 的两个出现位置形成两个独立的相邻对时,交换操作可以满足条件。例如,位置序列为 ...a, b, ..., a, b... 或 ...a, b, ..., b, a...,此时交换后 a 和 b 的两个位置均相邻。
方法思路
预处理位置:记录每个数字的两个出现位置。
遍历相邻元素:对于每对相邻元素 (a, b),检查它们的位置是否满足条件。
条件验证:收集四个位置并排序,检查是否形成两个独立的相邻区间。
去重存储:使用集合存储有效对,避免重复计数。
解决代码
#include <algorithm>
#include <iostream>
#include <set>
#include <vector>
using namespace std;int main() {int T;cin >> T;while (T--) {int N;cin >> N;vector<int> A(2 * N);for (auto& a : A) cin >> a;vector<vector<int>> position(N + 1);for (int i = 0; i < 2 * N; i++) {position[A[i]].push_back(i);}set<pair<int, int>> answers;for (int i = 0; i < 2 * N - 1; i++) {int a = A[i], b = A[i + 1];if (a == b) continue;// 检查a和b的两个位置是否不相邻if (position[a][0] + 1 == position[a][1] ||position[b][0] + 1 == position[b][1]) {continue;}// 收集四个位置并排序vector<int> v{position[a][0], position[a][1],position[b][0], position[b][1]};sort(v.begin(), v.end());// 检查是否满足条件if (v[0] + 1 == v[1] && v[2] + 1 == v[3]) {answers.emplace(min(a, b), max(a, b));}}cout << answers.size() << "\n";}return 0;
}