在通信网络优化领域,计算信号传输的最短延迟是一个经典问题。CCF-CSP认证考试的第35次第4题就给出了这样一个实际场景:我们需要在由基站和覆盖区域组成的网络中,找到从终端A到终端B的最短传输时间路径。
题目给出了两个关键数据结构:
信号传输的路径遵循"终端→基站→终端"的模式。这意味着我们需要计算:
这个问题本质上是单源最短路径问题,Dijkstra算法是自然的选择。但直接应用传统Dijkstra会遇到效率问题:
虚拟节点(virtual node)是一种图论中常用的优化技术,通过在原始图中添加辅助节点来简化连接关系。在本问题中:
这种设计巧妙地将O(n²)的边数降为O(n):
让我们验证这种建模的正确性:
假设节点A和B都在覆盖区域i内:
优化前后的复杂度对比:
| 方案 | 顶点数 | 边数 | Dijkstra复杂度 |
|---|---|---|---|
| 原始 | n | O(m*n²) | O(m*n² + n²) |
| 虚拟节点 | n+m | O(m*n) | O(m*n log(n+m)) |
对于n,m=5000的情况:
cpp复制const int N=5005*2, INF=0x3f3f3f3f;
vector<pii> g[N]; // 邻接表
int dis[N]; // 最短距离
int vis[N]; // 访问标记
选择这些数据结构的考虑:
cpp复制priority_queue<pii,vector<pii>,greater<pii>> q; // 小根堆
q.push({0,1}); // 距离0,节点1
while(!q.empty()) {
auto t = q.top(); q.pop();
int u = t.second, d = t.first;
if(vis[u]) continue;
vis[u] = 1;
for(auto edge:g[u]) {
int v = edge.first, w = edge.second;
if(dis[v] > d + w) {
dis[v] = d + w;
q.push({dis[v], v});
}
}
}
几个关键点:
cpp复制for(int i=1;i<=m;i++) {
int x,y,r,t; cin>>x>>y>>r>>t;
for(int j=1;j<=n;j++) {
int ax=node[j].first, ay=node[j].second;
if(ax>=x-r && ax<=x+r && ay>=y-r && ay<=y+r) {
g[j].push_back({n+i,0});
g[n+i].push_back({j,t});
}
}
}
这里有几个值得注意的实现细节:
错误1:虚拟节点编号冲突
错误2:边权设置错误
错误3:优先队列使用错误
输入输出加速
cpp复制ios::sync_with_stdio(0),cin.tie(0);
这对大规模数据输入可显著提升速度
内存预分配
cpp复制vector<pii> node(n+5);
提前预留空间避免频繁扩容
区域判断优化
可将矩形判断改为:
cpp复制if(abs(ax-x)<=r && abs(ay-y)<=r)
减少比较运算次数
如果区域是圆形而非矩形,如何高效判断点在圆内?
如果网络是动态变化的,如何优化?
如果需要处理负权边?
在实际通信网络设计中,这种虚拟节点思想可以延伸应用在:
以下是带有详细注释的完整实现,帮助理解每个关键部分:
cpp复制#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define pii pair<int,int>
const int N=5005*2, INF=0x3f3f3f3f;
vector<pii> g[N]; // 邻接表:存储{目标节点,边权}
int dis[N]; // 存储起点到各节点的最短距离
int vis[N]; // 标记节点是否已确定最短路径
void dijkstra() {
memset(dis, 0x3f, sizeof(dis)); // 初始化距离为INF
dis[1] = 0; // 起点到自己的距离为0
// 使用小根堆优化,按距离排序
priority_queue<pii, vector<pii>, greater<pii>> q;
q.push({0, 1}); // {距离,节点}
while(!q.empty()) {
auto t = q.top(); q.pop();
int u = t.second, d = t.first;
if(vis[u]) continue; // 已确定最短路径的节点跳过
vis[u] = 1;
// 遍历所有邻接节点
for(auto edge: g[u]) {
int v = edge.first, w = edge.second;
// 松弛操作
if(dis[v] > d + w) {
dis[v] = d + w;
q.push({dis[v], v});
}
}
}
}
void solve() {
int n, m;
cin >> n >> m;
vector<pii> node(n+5); // 存储基站坐标
// 读入基站坐标
for(int i=1; i<=n; i++)
cin >> node[i].first >> node[i].second;
// 建图:处理每个覆盖区域
for(int i=1; i<=m; i++) {
int x, y, r, t;
cin >> x >> y >> r >> t;
// 检查每个基站是否在区域内
for(int j=1; j<=n; j++) {
int ax = node[j].first, ay = node[j].second;
// 矩形区域判断
if(ax >= x-r && ax <= x+r && ay >= y-r && ay <= y+r) {
// 添加双向边
g[j].push_back({n+i, 0}); // 真实→虚拟,延迟0
g[n+i].push_back({j, t}); // 虚拟→真实,延迟t
}
}
}
dijkstra(); // 执行最短路径算法
// 输出结果
if(dis[n] == INF) cout << "Nan" << endl;
else cout << dis[n] << endl;
}
int main() {
ios::sync_with_stdio(0), cin.tie(0); // 加速IO
solve();
return 0;
}
虚拟节点技术在图算法中有着广泛的应用场景,以下是一些典型用例:
多层网络建模
状态转移问题
网络流问题
对于本题,还可以考虑以下变种:
如果延迟t与距离相关怎么办?
如果基站有容量限制怎么办?
如果要求k短路而不仅是最短路?
在实际工程实践中,这种优化技术可以显著提升系统性能。我曾在一个物流路径规划项目中应用类似技术,将计算时间从小时级降到秒级,关键点在于: