题目抽象
要求所有结点与源结点连通,使得所有边权之和最小。即求最小的最短路径树。
大致思路
算法:通过Dijkstra算法可以构建最短路径树,如何保证这棵树最小呢?这就需要一些变形了:
每个结点需要记录它的前驱边,每次松弛的条件是u.d + e ≤ v.d,若e比当前的前驱边短,则需要更新前驱边为e
实现:由于n达到了10^4,因此需要采用队列优化(邻接表储存Edge的编号)
#include <iostream>
#include <cstring>
#include <queue>
#include <vector>
using namespace std;
struct Edge
{
int beg,end,len;
Edge(int a, int b, int c):beg(a),end(b),len(c) {}
};
vector<Edge> edges;
int pi[10005];
struct Node
{
int num, d;
friend bool operator < (const Node& a, const Node& b)
{
if(a.d != b.d) return a.d > b.d;
return edges[pi[a.num]].len > edges[pi[b.num]].len;
}
Node(int a, int b):num(a),d(b) {}
};
const int inf = 1e8;
int n,m,a,b,c,cur;
vector<int> adj[10005];
priority_queue<Node> que;
bool vis[10005];
int d[10005];
void init()
{
que.push(Node(1,0));
for(int i=1; i<=n; ++i)
{
d[i] = inf;
pi[i] = -1;
}
d[1] = 0;
}
void addEdge(int a, int b, int c)
{
edges.push_back(Edge(a,b,c));
adj[a].push_back(edges.size()-1);
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m;
for(int i=0; i<m; ++i)
{
cin>>a>>b>>c;
addEdge(a,b,c);
addEdge(b,a,c);
}
init();
while(!que.empty())
{
cur = que.top().num;
que.pop();
if(vis[cur]) continue;
for(int i=0; i<adj[cur].size(); ++i)
{
Edge& e = edges[adj[cur][i]];
//核心点
if(vis[e.end]==0 && (d[e.end] > d[cur] + e.len || (d[e.end] == d[cur] + e.len && edges[pi[e.end]].len > e.len) ))
{
d[e.end] = d[cur] + e.len;
pi[e.end] = adj[cur][i];
que.push(Node(e.end, d[e.end]));
}
}
}
int sum = 0;
for(int i=1; i<=n; ++i)
if(pi[i] != -1) sum += edges[pi[i]].len;
cout<<sum;
return 0;
}