CCF 201609-4 交通规划 传送门
先放张图得瑟得瑟, 一遍过, so happy.
这是一道描述非常简单的题目, 看到题目就有点小开心, 觉得肯定能做出来的.
目的很清晰, 所有的点到首都的距离都是最短路, 那么最短路就要保存下来舍弃其他无用的边. 看数据范围, 就用SPFA算法了. 中心思想是记录最短路径+去除无用边
关键是一条路的最短路可能会有好几条, 那么应该怎么选呢? 如果停留在表面, 会觉得这道题目非常的复杂, 因为选出这些边, 似乎会影响后面的结果, 到底选哪些边? 计算机可不知道, 这里面一定有一个规则.
画张图, 仔细观察后会发现, 一个点可以有几个出度(也可以有0个), 但是只会有一个入度. 因为>1的话是无意义的. 那么很清晰了, 我们选出每个点入边里面最短的边即可, 也就是总共选出n - 1条边. 排个序来选就OK.
#include <algorithm>
#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
struct Node {
int to, w;
Node (int t, int w) : to(t), w(w) {}
};
struct Edge {
int from, to, w;
Edge (int f, int t, int w) : from(f), to(t), w(w) {}
};
const int maxn = 10000 + 5;
const int INF = 0x3f3f3f3f;
vector<Node> G[maxn], last[maxn];
vector<Edge> edge;
int inque[maxn] = {}, n, m, dis[maxn], ans = 0;
bool cmp(Edge x, Edge y)
{
if (x.to != y.to) return x.to < y.to;
return x.w < y.w;
}
void SPFA()
{
queue<int> Q;
Q.push(1);
inque[1] = true;
dis[1] = 0;
while (!Q.empty()) {
int now = Q.front();
inque[now] = false;
Q.pop();
for (int i = 0; i < G[now].size(); ++i) {
int v = G[now][i].to, w = G[now][i].w;
if (dis[v] == dis[now] + w) {
last[v].push_back(Node(now, w));
} else if (dis[v] > dis[now] + w) {
last[v].clear();
last[v].push_back(Node(now, w));
dis[v] = dis[now] + w;
if (!inque[v]) {
Q.push(v);
inque[v] = true;
}
}
}
}
}
int main()
{
memset(dis, 0x3f, sizeof(dis));
cin >> n >> m;
for (int i = 1, u, v, w; i <= m; ++i) {
cin >> u >> v >> w;
G[u].push_back(Node(v, w));
G[v].push_back(Node(u, w));
}
SPFA();
for (int i = 1; i <= n; ++i) {
for (int j = 0; j < last[i].size(); ++j) {
edge.push_back(Edge(last[i][j].to, i, last[i][j].w));
}
}
sort(edge.begin(), edge.end(), cmp);
int to = 2;
for (int i = 0; i < edge.size(); ++i) {
if (edge[i].to == to) {
ans += edge[i].w;
to++;
}
}
cout << ans;
}