【洛谷习题】公路修建

本想着做完这道题,就暂时告别MST,但越到了这个时候,惊喜也就越多,呵呵。

题目链接:https://www.luogu.org/problemnew/show/P1265


哎,上去就mengbi第二个规则,自己试了好半天,始终举不出例子来,只好去看题解,,,真想吐血!原来第二种情况根本不存在,每个城市都向离自己最近的城市修公路,怎么会出现环呢?这道题其实也是要求最小生成树!

好,Kruskal上!砰!

n<=5000?那么边数最多可有25000000!显然内存方面就会炸。

再仔细一看题解,哦,是用Prim。记得曾经dalao和我说过,Prim并不是一无是处,他就用到过一次,当时没仔细听,没想到最终还是翻了船。

去啃Prim,发现确实Kruskal不能完全代替他。具体详见另一篇博客《Prim算法》。

仔细想想,其实题目的描述就是Prim算法的过程,每次找一个和已加入最小生成树的点距离最近的点加入最小生成树。

边是不可以全部保存的,而Prim算法的过程刚好可以不用保存每条边,只需动态查询边的长度即可。Prim适用于稠密图,所以这道题非Prim莫属了。

高高兴兴写上Prim提交。。。什么!只有10分?找了好半天都不知道哪错了,摁着正解使劲读,最后才意识到求两点间距离时原来会溢出。

比如:sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2)),万一坐标的范围很大,中间过程(像(x1-x2)*(x1-x2))就会溢出!

唔,可算是完了。

 1 #include <cstdio>
 2 #include <cmath>
 3 #include <queue>
 4 
 5 using namespace std;
 6 
 7 const int maxn = 5005;
 8 const double inf = 1e16;
 9 
10 int x[maxn], y[maxn], vis[maxn];
11 double dist[maxn];
12 
13 inline long long pw2(int x) {
14     return 1LL * x * x;
15 }
16 
17 inline double dis(int i, int j) {
18     return sqrt(pw2(x[i] - x[j]) + pw2(y[i] - y[j]));
19 }
20 
21 struct node {
22     int id;
23     double dist;
24     node(int i, double d) : id(i), dist(d) {}
25     bool operator < (const node& rhs) const {
26         return dist > rhs.dist;
27     }
28 };
29 
30 priority_queue<node> q;
31 
32 int main() {
33     int n;
34     scanf("%d", &n);
35     for (int i = 1; i <= n; ++i) scanf("%d%d", &x[i], &y[i]);
36     double ans = 0;
37     for (int i = 2; i <= n; ++i) dist[i] = inf;
38     q.push(node(1, 0));
39     while (!q.empty()) {
40         int u = q.top().id;
41         q.pop();
42         if (vis[u]) continue;
43         vis[u] = 1;
44         ans += dist[u];
45         for (int v = 1; v <= n; ++v) {
46             if (v == u) continue;
47             if (dist[v] > dis(u, v)) {
48                 dist[v] = dis(u, v);
49                 q.push(node(v, dist[v]));
50             }
51         }
52     }
53     printf("%.2f", ans);
54     return 0;
55 }
AC代码
原文地址:https://www.cnblogs.com/Mr94Kevin/p/9575468.html