将特征数对建模为有向图中的边,转化为用最少有向路径覆盖所有边,利用并查集分连通块结合度数差统计欧拉路径段数。
OJ: roj
题目 ID: 1534
难度:提高
标签:图论欧拉图并查集
创建: 2026-09-30 16:41
更新: 2026-10-07 11:01
形式化题目
给定
正解
思路
将每个特征数对
序列中相邻的元素
因此,要使序列总长度最小,等价于用最少条数的有向路径覆盖图中的所有边。
图由若干个互不相连的弱连通分量组成,每个连通分量内部必须独立覆盖。对于某一个弱连通分量
- 统计每个顶点的出度
和入度 。 - 在任意一条有向路径中,只有起点满足出度比入度多 1,终点满足入度比出度多 1,中间点出入度相等。因此,若一个节点满足
,它必须至少作为 条路径的起点。 - 记连通分量
中所有顶点的正度数差之和为: - 若
,说明分量内无法形成欧拉回路,最少需要 条路径覆盖所有边。 - 若
,说明分量内所有节点出度均等于入度,图存在欧拉回路,此时可以用 条闭合路径覆盖所有边。 因此,该分量所需的最少路径数为:
- 若
整张图所需的最少路径数为各个弱连通分量所需路径数之和:
最终答案即为:
使用并查集维护顶点的弱连通性,遍历边统计出入度后,按连通块求和即可。
代码
Python 版:
#!/usr/bin/env python3
# Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
# rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
# rainboy的学习导航网站: https://idx.roj.ac.cn
# create_at: 2026-04-19 12:00
# update_at: 2026-04-19 12:00
import sys
from collections import defaultdict, deque
def find_root(parent: dict[int, int], x: int) -> int:
"""并查集查找代表元(带路径压缩)。"""
while parent[x] != x:
parent[x] = parent[parent[x]]
x = parent[x]
return x
def union_sets(parent: dict[int, int], x: int, y: int) -> None:
"""合并两个节点所在的连通分量。"""
root_x, root_y = find_root(parent, x), find_root(parent, y)
if root_x != root_y:
parent[root_x] = root_y
def count_extra_edges(in_deg: dict[int, int], out_deg: dict[int, int], nodes: list[int]) -> int:
"""计算单个弱连通块为满足欧拉路径需要补齐的额外有向边数。"""
pos_diff = sum(max(0, out_deg[u] - in_deg[u]) for u in nodes)
return max(1, pos_diff)
def solve() -> None:
data = iter(map(int, sys.stdin.buffer.read().split()))
try:
m = next(data)
except StopIteration:
return
in_deg: dict[int, int] = defaultdict(int)
out_deg: dict[int, int] = defaultdict(int)
parent: dict[int, int] = {}
edges_count = m
for _ in range(m):
u, v = next(data), next(data)
out_deg[u] += 1
in_deg[v] += 1
parent.setdefault(u, u)
parent.setdefault(v, v)
union_sets(parent, u, v)
components: dict[int, list[int]] = defaultdict(list)
for node in parent:
components[find_root(parent, node)].append(node)
extra_edges = sum(count_extra_edges(in_deg, out_deg, nodes) for nodes in components.values())
print(edges_count + extra_edges)
if __name__ == "__main__":
solve()C++ 版(同一算法):
/**
* Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
* rbook: -> https://rbook.roj.ac.cn https://rbook2.roj.ac.cn
* rainboy的学习导航网站: https://idx.roj.ac.cn
* create_at: 2026-10-05 07:01
* update_at: 2026-10-05 07:01
*/
#include <cstdio>
typedef long long ll;
const int MAXV = 1005; // 节点编号上限 1000,多开几个位置
int parent[MAXV]; // 并查集:parent[x] 为 x 所在弱连通分量的代表元
ll in_deg[MAXV]; // in_deg[u] 表示节点 u 的入度
ll out_deg[MAXV]; // out_deg[u] 表示节点 u 的出度
ll pos_sum[MAXV]; // pos_sum[root] 表示该连通块内所有 out>in 节点的 (out-in) 之和
int used[MAXV]; // used[u]=1 表示节点 u 在输入中出现过
// 并查集查找,带路径压缩
int find_root(int x) {
while (parent[x] != x) {
parent[x] = parent[parent[x]];
x = parent[x];
}
return x;
}
// 合并两个节点所在的弱连通分量
void union_sets(int x, int y) {
int rx = find_root(x);
int ry = find_root(y);
if (rx != ry) {
parent[rx] = ry;
}
}
int main() {
int m;
if (scanf("%d", &m) != 1) {
return 0;
}
for (int i = 1; i < MAXV; i++) {
parent[i] = i;
}
for (int i = 1; i <= m; i++) {
int l, r;
scanf("%d%d", &l, &r);
out_deg[l]++;
in_deg[r]++;
used[l] = 1;
used[r] = 1;
union_sets(l, r);
}
// 把每个节点出度比入度多出的部分累加到所在连通块的代表元上
for (int u = 1; u < MAXV; u++) {
if (used[u] == 1 && out_deg[u] > in_deg[u]) {
pos_sum[find_root(u)] += out_deg[u] - in_deg[u];
}
}
// 每个连通块至少需要 1 条路径覆盖,若正度数差更大则按正度数差算
ll path_count = 0;
for (int u = 1; u < MAXV; u++) {
if (used[u] == 1 && parent[u] == u) {
ll delta = pos_sum[u];
if (delta < 1) {
delta = 1;
}
path_count += delta;
}
}
// 每条路径有 e 条边就需要 e+1 个元素,所有路径合计为 m + 路径数
printf("%lld\n", (ll)m + path_count);
return 0;
}复杂度
- 时间复杂度:
,其中 为特征数对数量, 为出现的不同节点数量( )。并查集合并与度数统计均在毫秒级内完成。 - 空间复杂度:
,存储节点出入度与并查集结构。
图示解析
以样例数据为例: 样例输入包含 12 条边,构成的图分为若干局部路径,通过计算各点的出入度差值可确定欧拉路径划分方案。
flowchart LR
subgraph 连通块
8 --> 5 --> 7 --> 6
5 --> 1 --> 4 --> 5
4 --> 2 --> 3 --> 9 --> 6
2 --> 8 --> 6
end
观察各节点的入度与出度:
- 节点
:出度 2,入度 1,差值 - 节点
:出度 2,入度 1,差值 - 节点
:出度 2,入度 1,差值 - 节点
:出度 0,入度 3,差值 - 其余节点出度与入度均相等。
全图为单个连通分量,正度数差之和为
总结
本题的关键在于通过模型转换,将“求最短重叠序列”转化为经典的“有向图最少有向路径覆盖所有边”问题。结合欧拉图性质,单个弱连通块的最少路径数取决于度数不平衡顶点的差值,再通过并查集对弱连通分量分别处理,最终得到简单且高效的线性阶解法。