
实现代码
#include <iostream>
#include <vector>
#include <queue>
#include <utility> // for std::pair
#include <cstring> // for memset
using namespace std;
const int MAXN = 5010, INF = 0x3f3f3f3f;
vector<pair<int, int>> g[MAXN]; // 改为邻接表存储图
int dist[MAXN], n, m, res;
bool book[MAXN];
void prim()
{
memset(book, false, sizeof(book));
for (int i = 1; i <= n; ++i) dist[i] = INF;
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
dist[1] = 0;
pq.push(make_pair(0, 1));
while (!pq.empty())
{
pair<int, int> top = pq.top();
pq.pop();
int d = top.first;
int u = top.second;
if (book[u]) continue;
book[u] = true;
res += d;
for (size_t i = 0; i < g[u].size(); ++i)
{
int v = g[u][i].first;
int w = g[u][i].second;
if (!book[v] && w < dist[v])
{
dist[v] = w;
pq.push(make_pair(w, v));
}
}
}
}
int main()
{
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int a, b, w;
cin >> a >> b >> w;
g[a].emplace_back(b, w);
g[b].emplace_back(a, w);
}
res = 0;
prim();
bool all_connected = true;
for (int i = 1; i <= n; ++i)
{
if (!book[i])
{
all_connected = false;
break;
}
}
if (!all_connected)
cout << "orz";
else
cout << res;
return 0;
}
通过这段代码我们来学习一下C++11以及C++STL
1 关于 vector<pair<int, int>> g[MAXN];
vector<pair<int, int>> g[MAXN];
是一个数组,每个元素是一个向量(vector),这个向量存储了若干个 pair<int, int>
类型的数据。让我们详细解释这个声明:
逐层解析
pair<int, int>
:
pair<int, int>
是一个模板类,它存储一对值,这里的每个值都是int
类型。比如,一个pair<int, int>
可以表示一条边,其中第一个int
是边连接的顶点,第二个int
是该边的权重。
vector<pair<int, int>>
:
vector<pair<int, int>>
是一个向量,它的每个元素都是pair<int, int>
类型。这个向量可以存储一个顶点的所有邻接边的信息。
vector<pair<int, int>> g[MAXN];
:
g[MAXN]
是一个长度为MAXN
(即 1000)的数组。数组的每个元素都是一个vector<pair<int, int>>
,即存储了一个顶点的所有邻接边。g[i]
表示图中第i
个顶点的所有邻接边。g[i][j]
表示从顶点i
出发的第j
条边,这条边用一个pair<int, int>
表示,pair
的第一个元素是该边的终点顶点,第二个元素是该边的权重。
示例
假设我们有一个简单的图,有 3 个顶点和 3 条边:
- 边 1: 顶点 1 到顶点 2,权重为 5
- 边 2: 顶点 1 到顶点 3,权重为 10
- 边 3: 顶点 2 到顶点 3,权重为 3
我们可以用以下代码填充 g
数组:
g[1].emplace_back(2, 5); // 顶点 1 到顶点 2,权重 5
g[1].emplace_back(3, 10); // 顶点 1 到顶点 3,权重 10
g[2].emplace_back(3, 3); // 顶点 2 到顶点 3,权重 3
g[2].emplace_back(1, 5); // 顶点 2 到顶点 1,权重 5 (无向图)
g[3].emplace_back(1, 10); // 顶点 3 到顶点 1,权重 10 (无向图)
g[3].emplace_back(2, 3); // 顶点 3 到顶点 2,权重 3 (无向图)
最终,g
数组将如下所示:
g[1]
包含两条边:(2, 5)
和(3, 10)
g[2]
包含两条边:(1, 5)
和(3, 3)
g[3]
包含两条边:(1, 10)
和(2, 3)
总结
vector<pair<int, int>> g[MAXN];
用于表示图的邻接表,其中 g[i]
是一个向量,存储了从顶点 i
出发的所有边的终点和权重信息。这种表示方法在处理稀疏图(即边数远少于顶点数的图)时非常高效。
2 关于 emplace_back
emplace_back
是 C++11 引入的一个 STL 容器方法,用于在容器(如 vector
、list
等)的末尾直接构造元素。与 push_back
相比,emplace_back
更高效,因为它可以直接在目标位置构造对象,而不需要先构造临时对象再复制或移动到容器中。
emplace_back
的使用示例
#include <vector>
#include <utility>
#include <iostream>
int main() {
std::vector<std::pair<int, int>> vec;
// 使用 push_back 插入元素
vec.push_back(std::make_pair(1, 2));
// 使用 emplace_back 插入元素
vec.emplace_back(3, 4);
// 输出向量内容
for (const auto& p : vec) {
std::cout << "(" << p.first << ", " << p.second << ")\n";
}
return 0;
}
在上述代码中:
vec.push_back(std::make_pair(1, 2));
首先用std::make_pair
构造一个pair<int, int>
对象,然后将其复制或移动到vec
的末尾。vec.emplace_back(3, 4);
直接在vec
的末尾构造一个pair<int, int>
对象。
主要区别
push_back
:需要先构造临时对象,再将该对象复制或移动到容器中,涉及一次复制或移动操作。emplace_back
:直接在容器末尾构造对象,不需要额外的复制或移动操作。
效率比较
emplace_back
通常比 push_back
更高效,尤其是在对象构造比较复杂或对象不支持移动语义的情况下,因为它省去了临时对象的创建和复制/移动的过程。
具体到你的代码
g[a].emplace_back(b, w);
g[b].emplace_back(a, w);
这里 g[a].emplace_back(b, w);
和 g[b].emplace_back(a, w);
的作用是直接在 g[a]
和 g[b]
的末尾构造一个 pair<int, int>
,对应的边分别是 (b, w)
和 (a, w)
。这样可以高效地将边信息添加到邻接表中。
3 关于 priority_queue的定义
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
这行代码定义了一个名为 pq
的优先队列。优先队列是一种特殊的队列结构,能够以高效的方式访问并移除具有最高优先级的元素。让我们详细解释这个定义:
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
逐部分解释
priority_queue<...>
:
- 这是优先队列的模板类,定义了一个优先队列。
pair<int, int>
:
- 优先队列中存储的元素类型是
pair<int, int>
。每个元素是一个整数对。
vector<pair<int, int>>
:
- 这是底层容器类型,指定优先队列内部使用
vector
作为存储数据的容器。优先队列通常使用堆(heap)结构实现,而vector
是一个动态数组,非常适合堆的实现。
greater<pair<int, int>>
:
- 这是一个比较函数对象,定义了优先队列的排序方式。默认情况下,
priority_queue
是一个最大堆,意味着最大元素具有最高优先级。如果你想要一个最小堆(即最小元素具有最高优先级),可以使用greater<...>
进行比较。这里的greater<pair<int, int>>
表示优先队列按照pair<int, int>
中的第一个元素进行升序排序。
综上所述
这行代码定义了一个优先队列 pq
,它的元素是 pair<int, int>
类型。优先队列内部使用 vector
作为底层容器,并且按照 pair<int, int>
的第一个元素进行升序排序,即最小堆。
应用场景
在我的代码中,这个优先队列用于 Prim 算法。pair<int, int>
中,第一个 int
表示边的权重(优先级),第二个 int
表示顶点编号。通过使用 greater<pair<int, int>>
,优先队列总是将权重最小的边放在顶部,从而方便 Prim 算法每次选择最小权重的边扩展生成树。
示例
假设你往 pq
中插入以下元素:
pq.push(make_pair(10, 1));
pq.push(make_pair(5, 2));
pq.push(make_pair(20, 3));
pq.push(make_pair(1, 4));
此时,优先队列的顺序是:
(1, 4)
(5, 2)
(10, 1)
(20, 3)
每次调用 pq.top()
时,将返回权重最小的元素 (1, 4)
,从而帮助 Prim 算法选择当前最小的边进行扩展。
Comments NOTHING