题目分析
从代码来看,这道题很可能是一个关于图的最小生成树问题(结合代码中的并查集和边的处理),并且题目中边可能有不同的类型(根据输入中的 t
来区分)。目标可能是求出图的最小生成树的总权重,其中某些边是必须包含在最小生成树中的(t == 1
的边),而其他边是可选的(t == 2
的边)。
代码逐段分析
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 2010, M = 10010;
int n, m;
struct Edge
{
int a, b, w;
bool operator< (const Edge &t) const
{
return w < t.w;
}
}e[M];
int p[N];
- 包含必要的头文件:
cstring
用于字符串处理(这里未用到),iostream
用于输入输出,algorithm
用于排序等算法。 - 定义了两个常量
N
和M
,分别表示节点的最大数量和边的最大数量。 - 声明了变量
n
和m
,用于存储图的节点数和边数。 - 定义了结构体
Edge
,用于表示图的边,包含两个端点a
和b
以及边的权重w
。重载了小于运算符<
,方便后续对边按权重进行排序。 - 定义了数组
p
,用于实现并查集,p[x]
表示节点x
的父节点。
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
- 实现了并查集的
find
函数,用于查找节点x
的根节点。通过递归的方式不断向上查找父节点,同时进行路径压缩,将节点x
直接指向其根节点,提高后续查找效率。
int main()
{
cin >> n >> m;
for (int i = 1; i <= n; i ++ ) p[i] = i;
int res = 0, k = 0;
for (int i = 0; i < m; i ++ )
{
int t, a, b, w;
cin >> t >> a >> b >> w;
if (t == 1)
{
res += w;
p[find(a)] = find(b);
}
else e[k ++ ] = {a, b, w};
}
- 在
main
函数中,首先读取节点数n
和边数m
。 - 初始化并查集,将每个节点的父节点设为自身。
- 定义变量
res
用于存储最小生成树的总权重,k
用于记录可选边的数量。 - 遍历所有的边,读取每条边的类型
t
、两个端点a
和b
以及权重w
。 - 如果边的类型
t == 1
,表示该边是必须包含在最小生成树中的,将其权重加到res
中,并通过并查集将两个端点所在的集合合并。 - 如果边的类型
t != 1
,即t == 2
,表示该边是可选的,将其存储到数组e
中,并增加可选边的数量k
。
sort(e, e + k);
for (int i = 0; i < k; i ++ )
{
int a = find(e[i].a), b = find(e[i].b), w = e[i].w;
if (a != b)
{
p[a] = b;
res += w;
}
}
- 对可选边数组
e
按照权重进行排序,使用sort
函数,排序的范围是从e
到e + k
。 - 遍历排序后的可选边数组,对于每条边,查找其两个端点在并查集中的根节点
a
和b
。 - 如果两个根节点不相同,说明这条边不会形成环,可以加入到最小生成树中。将两个集合合并(
p[a] = b
),并将边的权重加到res
中。
cout << res << endl;
return 0;
}
- 输出最小生成树的总权重
res
。 - 程序结束,返回 0。
时间复杂度分析
- 初始化并查集的时间复杂度为 O(n),因为需要将每个节点的父节点设为自身。
- 读取边并处理必选边的时间复杂度为 O(m),因为需要遍历每一条边。
- 对可选边进行排序的时间复杂度为 O(klogk),其中
k
是可选边的数量,k <= m
。 - 遍历可选边并处理的时间复杂度为 O(k),每次查找根节点的操作时间复杂度近似为 O(1)(经过路径压缩后),合并操作的时间复杂度也近似为 O(1)。
因此,总的时间复杂度为 O(n+m+klogk),近似为 O(mlogm),因为 k≤m。
空间复杂度分析
- 存储节点的并查集数组
p
需要 O(n) 的空间。 - 存储边的数组
e
最多需要 O(m) 的空间。
因此,总的空间复杂度为 O(n+m)。