这道题一开始想的是用Dijkstra或者是SPFA算法来写,写好了以后提交,结果TLE了。当时写的时候就感觉每个节点更新的复杂度至少是 O(n) 级别的,再考虑到数据量有 N<=100000,0<=Xi,Yi<=1000000000 就感觉会超时,不过当时也不知道该怎么降维,只能硬着头皮上了。后来看了hihocoder上面的题解后
知道了怎么将 O(n) 降为 O(4) ,重新写了以后,就AC了。
首先我来讲讲 O(n) 的算法是个什么思路,不想看的朋友可以直接看下一段。在SPFA算法中,在某个节点 i 从队列中弹出来后,我们需要将跟这个节点相连的节点最短路径进行更新,然后将路径长度变短的节点重新塞入队列中。这样一来,由于每个节点都可以跟节点 i 相连,所以需要将所有的节点都进行一遍更新,也就是每个节点的更新复杂度是 O(n) ,在这样大的数据量的情况下,跑完整个list肯定就会超时了。因此现在就需要降维。
看了hihocoder的题解,简单来说,就是分别找到在X和Y上距离每个节点最近的两个节点(一前一后),然后在每次更新相邻节点时,就只用更新前后4个,也就是 O(4) 的复杂度了。因此我们需要对X进行排序,找到该节点的一前一后两个点(X数值相同也行);同理对Y进行处理。之后每次更新只用找到这4个点更新就行。为了更进一步进行优化,我还做了剪枝处理,设置一个1到N的距离最大值maxDistance,如果两个点的距离大于这个值,就直接丢弃,不需要在访问了。并且需要对maxDistance进行维护,更新。下面就是AC代码:
//Islands Trave
#include<iostream>
//#include<map>
//#include<string>
#include<queue>
#include<vector>
#include <cstdio>
#include<fstream>
#include<algorithm>
using namespace std;
#define Min(a,b) a<b?a:b
#define INF 1000000005
#define Num 100000+5
typedef struct {//每个节点的结构
int X;
int Y;
int ID;
int LinkNode[4];//分别对应X上的一前一后和Y上的一前一后,beforeX, afterX,beforeY, afterY
} Location;
Location location[Num];
int head[Num];
queue<int> list;
bool visit[Num];
int Distance[Num];
int differ(int l1, int l2)//计算两个节点的距离
{
if (l1 == l2) return INF;
return Min(abs(location[l1].X - location[l2].X), abs(location[l1].Y - location[l2].Y));
}
bool compx(Location l1, Location l2)///按照X对节点进行排序
{
if (l1.X != l2.X) return l1.X < l2.X;
else return l1.Y < l2.Y;
}
bool compy(Location l1, Location l2)//按照Y对节点进行排序
{
if (l1.Y != l2.Y) return l1.Y < l2.Y;
else return l1.X < l2.X;
}
bool compID(Location l1, Location l2)//按照ID对节点进行排序
{
return l1.ID < l2.ID;
}
int LuShi()
{
int N = 0, maxDistance = 1000000000;
cin >> N;
int i = 0, j = 0;
for (i = 1; i < N + 1; i++)
{
cin >> location[i].X >> location[i].Y;
location[i].ID = i;
visit[i] = false;
Distance[i] = INF;
}
sort(location + 1, location + N + 1, compx);//对X进行排序
i = 1;
location[i].LinkNode[0] = 0;
while (i<N + 1)
{
j = i + 1;
location[j].LinkNode[0] = location[i].ID;//beforeX,在X上的前一个
location[i].LinkNode[1] = j <= N ? location[j].ID : 0;//afterX,在X上的后一个
i++;
}
sort(location + 1, location + N + 1, compy);//对Y进行排序
i = 1;
location[j].LinkNode[2] = 0;
while (i < N + 1)
{
j = i + 1;
location[j].LinkNode[2] = location[i].ID;//beforeY,在Y上的前一个
location[i].LinkNode[3] = j <= N ? location[j].ID : 0;//afterY,在Y上的后一个
i++;
}
sort(location + 1, location + N + 1, compID);//恢复原来的顺序,方便查找
maxDistance = differ(1,N);//计算最大值,准备剪枝
for (int i = 1; i < N + 1; i++)//对1节点进行更新,将周围4个节点塞入队列
{
if (differ(1, i)<maxDistance) {
Distance[i] = differ(1, i);
list.push(i);
visit[i] = true;
}
}
while (!list.empty())
{
for (i = 0; i < 4; i++)//可以看到,这个复杂度是O(4)
{
int to = location[list.front()].LinkNode[i];//相邻的节点
int min = differ(list.front(), to) + Distance[list.front()];//计算1到该节点的最短距离
if (min<maxDistance) {//如果这一跳距离大于1到N的距离,那么最后通过这个路径到达N的距离一定大于等于maxDistance,剪枝
if (min < Distance[to]) {//只有小于原来的距离才剪枝
if (to == N) {//如果相邻节点是N,更新最短距离maxDistance
maxDistance = min;
}
else//不是到N的,将这个节点塞入队列
{
if (visit[to] == false) {//如果在队列中,就不需要塞了
list.push(to);
visit[to] = true;
}
}
Distance[to] = min;//更新相邻节点的最短距离
}
}
}
visit[list.front()] = false;//弹出节点
list.pop();
}
cout << Distance[N] << endl;//直接输出N的值
return 0;
}
int main()
{
streambuf * oldbuf = cin.rdbuf((new ifstream("C:\\Users\\yzc\\Desktop\\input.txt"))->rdbuf());//重定向,OJ时将它注释掉
//cout << LuShi() << endl;
LuShi();
system("pause");
return 0;
}