P1265 公路修建 优先队列优化prim
解题思路
问题分析
-
题目描述:有n个城市,需要修建公路将它们全部连通。修建规则特殊:
-
每轮每个城市选择最近的城市申请修路
-
有特殊规则处理冲突和环路情况
-
最终目标是计算所有修建公路的总长度
-
-
关键发现:虽然题目描述复杂,但实际可以简化为标准的最小生成树问题。因为:
-
题目描述的修建过程实际上会构建一棵生成树
-
特殊规则保证了不会形成环路,且总长度最小
-
算法选择
-
Prim算法:适合稠密图的最小生成树算法
-
本题是完全图(每两个城市间都有潜在的公路)
-
Prim算法时间复杂度O(n²),对于n≤5000可以接受
-
使用优先队列优化查找最小边的过程
-
-
为什么不用Kruskal:
-
Kruskal需要对所有边排序,完全图有O(n²)条边
-
对于n=5000,边数约2500万,排序和处理的代价太高
-
实现细节
-
距离计算:直接计算每对城市间的欧几里得距离
-
优先队列:存储(距离,城市编号)对,快速获取最小距离城市
-
标记数组:避免重复处理同一城市
-
距离更新:每当新城市加入生成树,更新所有未加入城市到生成树的距离
复杂度分析
-
时间复杂度:
-
每个城市处理一次:O(n)
-
每次处理需要更新n-1个城市的距离:O(n)
-
优先队列操作:O(log n)
-
总体:O(n²)
-
-
空间复杂度:
-
存储坐标:O(n)
-
距离数组和标记数组:O(n)
-
优先队列:O(n)
-
正确性证明
-
Prim算法保证:能够找到连接所有顶点的最小生成树
-
题目规则对应:
-
"每个城市选择最近城市"对应Prim的贪心选择
-
"避免环路"由生成树性质保证
-
"多城市冲突"由最短距离优先保证
-
优化空间
-
距离计算优化:可以预先计算所有距离,但空间需求大
-
更高效堆:使用Fibonacci堆可以优化到O(m + n log n),但实现复杂
-
并行计算:对于大规模数据,可以并行处理距离计算
#include<bits/stdc++.h> #define pii pair<double,int> // 定义一个pair类型,存储(距离,顶点编号) using namespace std; const int N = 1e5 + 10, inf = 0x3f3f3f3f; vector<pii> g[N]; // 邻接表(虽然本题未使用邻接表存储图) int n, m; // n:城市数量,m:未使用 double dis[N]; // 存储各点到当前生成树的最小距离 int vis[N]; // 标记数组,记录顶点是否已加入生成树 int x[N], y[N]; // 存储每个城市的坐标 // 计算两个城市间的欧几里得距离 double dist(int i, int j) { return sqrt((x[i]-x[j])*(x[i]-x[j]) + (y[i]-y[j])*(y[i]-y[j])); } // Prim算法实现 void prim() { // 初始化距离数组 for(int i = 1; i <= n; i++) dis[i] = 1e12; // 不能用memset初始化double数组 dis[1] = 0; // 从城市1开始构建生成树 double sum = 0, ans = 0; // sum:已加入城市数,ans:公路总长度 priority_queue<pii, vector<pii>, greater<pii>> q; // 最小堆 q.push({0, 1}); // 初始放入城市1,距离0 while(!q.empty()) { int x = q.top().second; // 取出当前距离最小的城市 q.pop(); if(vis[x]) continue; // 如果已访问过则跳过 vis[x] = 1; // 标记为已访问 ans += dis[x]; // 累加这条公路的长度 sum++; // 已加入城市数+1 // 更新所有其他城市到生成树的距离 for(int i = 1; i <= n; i++) { if(i == x) continue; // 跳过自身 double d = dist(x, i); // 计算距离 if(dis[i] > d) { // 如果找到更小的距离 dis[i] = d; // 更新距离 q.push({dis[i], i}); // 加入优先队列 } } } printf("%.2f", ans); // 输出总长度,保留两位小数 } int main() { cin >> n; // 读取每个城市的坐标 for(int i = 1; i <= n; i++) { cin >> x[i] >> y[i]; } prim(); // 执行Prim算法 return 0; }

浙公网安备 33010602011771号