Who's Studio.

hihoCoder#1138 - Islands Travel

Word count: 806Reading time: 4 min
2016/05/10

题目

给定N个节点,坐标分别为(X1, Y1),...(XN, YN),任意两点之间的距离定义为min{|Xi - Xj|, |Yi| - Yj},求X1到XN的最短距离。
具体描述请见hihoCoder

解题思路

本题如果采用暴力解法,直接计算出所有点对距离然后采用Dijkstra或者SPFA是不行的,因为这是一个完全图,光是计算所有点对间的距离时间复杂度就已经是N2了。因此需要优化Dijkstra算法的输入数据。采用数学知识进行分析,可以发现对任意一个点,我们只需要考虑x轴上一左一右离它最近的两个点,以及y轴上一上一下离它最近的两个点。这样一来边数就从N2减到了4N,采用堆实现的Dijkstra即可。

时间复杂度

堆实现的Dijkstra的时间复杂度为MlogN,其中M = 4N,所以时间复杂度为NlogN。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include <iostream>
#include <vector>
#include <queue>
#include <bitset>
#include <algorithm>
#include <cmath>
using namespace std;

struct Node {
int id = -1;
int d = -1;
vector<int> neighbors;
vector<int> edges;
};

struct NodeCmp {
bool operator()(const Node &a, const Node &b) const {
return b.d < a.d;
}
};

int dijkstra(Node* graph, int N, int s, int t) {
bitset<100001> set;
set.reset();
priority_queue<Node, vector<Node>, NodeCmp> heap;
graph[s].d = 0;
set[s] = 1;
for (int i = 0; i < graph[s].neighbors.size(); i++) {
graph[graph[s].neighbors[i]].d = graph[s].edges[i];
heap.push(graph[graph[s].neighbors[i]]);
}

int i = 0;
while (i < N - 1) {
Node nearestNode = heap.top();
heap.pop();
if (set[nearestNode.id])
continue;
i++;
set[nearestNode.id] = 1;
for (int j = 0; j < nearestNode.neighbors.size(); j++) {
if (graph[nearestNode.neighbors[j]].d == -1 || nearestNode.edges[j] + nearestNode.d < graph[nearestNode.neighbors[j]].d)
graph[nearestNode.neighbors[j]].d = nearestNode.edges[j] + nearestNode.d;
heap.push(graph[nearestNode.neighbors[j]]);
}
}

return graph[t].d;
}

struct Point {
int id = 0;
int x = 0;
int y = 0;
Point(int i, int xv, int yv) {
id = i;
x = xv;
y = yv;
}
};

bool xComp(const Point &a, const Point &b) {
return a.x < b.x;
}

bool yComp(const Point &a, const Point &b) {
return a.y < b.y;
}

int main() {
int N = 0;
cin >> N;
vector<Point> coord;
for (int i = 1; i < N + 1; i++) {
int x = 0, y = 0;
cin >> x >> y;
coord.push_back(Point(i, x, y));
}
Node* graph = new Node[N + 1];
for (int i = 0; i < N + 1; i++)
graph[i].id = i;
sort(coord.begin(), coord.end(), xComp);
for (int i = 0; i < N - 1; i++) {
int a = coord[i].id;
int b = coord[i + 1].id;
int edge = min(abs(coord[i].x - coord[i + 1].x), abs(coord[i].y - coord[i + 1].y));
graph[a].neighbors.push_back(b);
graph[a].edges.push_back(edge);
graph[b].neighbors.push_back(a);
graph[b].edges.push_back(edge);
}
sort(coord.begin(), coord.end(), yComp);
for (int i = 0; i < N - 1; i++) {
int a = coord[i].id;
int b = coord[i + 1].id;
int edge = min(abs(coord[i].x - coord[i + 1].x), abs(coord[i].y - coord[i + 1].y));
graph[a].neighbors.push_back(b);
graph[a].edges.push_back(edge);
graph[b].neighbors.push_back(a);
graph[b].edges.push_back(edge);
}

cout << dijkstra(graph, N, 1, N) << endl;

return 0;
}

问题

  1. 上述代码堆的实现直接使用了C++ STL里的priority_queue,它的数据类型不能是指针。
  2. 每次priority_queue进队时都会调用Node结构体的复制构造器,在Node结构体复杂的时候很耗时,应尽量降低Node结构体的复杂度。
CATALOG
  1. 1. 题目
  2. 2. 解题思路
  3. 3. 时间复杂度
  4. 4. 代码
  5. 5. 问题