兔兔 的 题解 —— 2020 CSP-J 多校赛 T4

回家 (home)

题目限制

  • 内存限制:512 MiB
  • 时间限制:2000 ms
  • 文件输入输出
    • 输入文件:home.in
    • 输出文件:home.out

题目知识点

  • 最短路
  • 贪心

题目来源

2020 CSP-J 多校赛 T4


题目

题目背景

W Y WY WY 巨佬是个路痴,有一天他迷路了

题目描述

W Y WY WY 生活在一个 n × n n \times n n×n 的网格图中,他在 ( s x , s y ) (s_x, s_y) (sx,sy) 这个地方迷路了,他的家在 ( f x , f y ) (f_x, f_y) (fx,fy) 这个地方
W Y WY WY 可以用一分钟向上下左右任意一个方向移动一格
由于 W Y WY WY 是个路痴,所以他想要一些帮助
作为 W Y WY WY 的好友 14 14 14 14 14 14 在网格图中为 W Y WY WY 设置了一些传送门,其中第 i i i 个传送门的位置在 ( x i , y i ) (x_i, y_i) (xi,yi)
W Y WY WY 所在的位置与一个传送点 横坐标相同纵坐标相同 时,那么 W Y WY WY 就可以 瞬间传送到这个传送门所在的位置,且 不花费任何时间
W Y WY WY 想知道他回家的 最短用时 是多少

格式

输入格式 (home.in)

输入第一行,包含两个整数 n , m n, m n,m,表示 网格图的大小 和 传送门的个数
输入第二行,包含四个整数 s x , s y , t x , t y s_x, s_y, t_x, t_y sx,sy,tx,ty,表示 W Y WY WY 迷路的位置 和 W Y WY WY 家的位置
接下来 m m m 行,每行包含两个整数 x i , y i x_i, y_i xi,yi,表示 第 i i i 个传送门的位置

输出格式 (home.out)

输出只有一行,表示最短用时

样例

样例1

样例输入

5 3
1 1 5 5
1 2
4 1
3 3

样例输出

5

样例解释

W Y WY WY 的家在 ( 5 , 5 ) (5, 5) (5,5),他在 ( 1 , 1 ) (1, 1) (1,1) 这个地方迷路了
W Y WY WY 可以通过在 ( 4 , 1 ) (4, 1) (4,1) 的传送门直接传送到 ( 4 , 1 ) (4, 1) (4,1)
然后从 ( 4 , 1 ) → ( 4 , 2 ) → ( 4 , 3 ) → ( 5 , 3 ) → ( 5 , 4 ) → ( 5 , 5 ) (4, 1) \rightarrow (4, 2) \rightarrow (4, 3) \rightarrow (5,3) \rightarrow (5, 4) \rightarrow (5, 5) (4,1)(4,2)(4,3)(5,3)(5,4)(5,5),用时为 5 5 5,可以证明这是最短用时

样例2

样例输入

84 5
67 59 41 2
39 56
7 2
15 3
74 18
22 7

样例输出

42

提示

数据范围

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 1 0 9 1 \leq n \leq 10^9 1n109 0 ≤ m ≤ m i n ( 3 × 1 0 5 , n 2 ) 0 \leq m \leq min(3 \times 10^5, n^2) 0mmin(3×105,n2) 1 ≤ s x , s y , t x , t y , x i , y i ≤ 1 0 9 1 \leq s_x, s_y, t_x, t_y, x_i, y_i \leq 10^9 1sx,sy,tx,ty,xi,yi109


思路

  • 注意:以下说的 ,指 题目给出的起点,终点,和传送门

假设中途不经过传送门,则 s s s t t t 的距离就是 ∣ s x − t x ∣ + ∣ s y − t y ∣ \mid s_x - t_x \mid + \mid s_y - t_y \mid sxtx+syty,即两点的曼哈顿距离,而且不受中途经过的点影响
对于第 i i i 个传送门,从任意一个点 ( a , b ) (a, b) (a,b) 到它的距离就是 m i n ( ∣ a − x i ∣ , ∣ b − y i ∣ ) min(\mid a - x_i \mid, \mid b - y_i \mid) min(axi,byi),只需要到达传送门的 横坐标 或者 纵坐标 即可
我们可以把 每个点 与 每个点 之间连接一条边,跑一个 d i j k s t r a dijkstra dijkstra


分析

  • 注意:以下说的 ,指 题目给出的起点,终点,和传送门

由于 每个点 与 每个点 之间建一条边,会花费很多的空间和时间
可以把 每个传送门 进行按照 x x x 坐标 和 y y y 坐标 排序
分别把 第 i i i 个传送门 和 第 i + 1 i + 1 i+1 传送门连一条边,每个传送门与 起点和终点 连一条边
那么为什么可以这样做呢?以 x x x 坐标排序 ( y y y 坐标排序 同理) 为例:
假设 W Y WY WY 要从 A A A 传送门 到 B B B 传送门,必定要经过 A x A_x Ax B x B_x Bx 之间的横坐标
若中途有另外的传送门 C C C,就可以从 A A A C C C,再从 C C C B B B,这样 A A A B B B 的距离也不会改变
(只需要在横坐标相同的点中考虑一个点,因为这些点是可以随意到达的;再把选中的这个点与其他的点连接即可,只是可以不需要把每个点都相连,只把横坐标相邻的点连边即可,因为每个点可以通过相邻的点到达其它点)
于是连接排序过后相邻的点,便可以从每个点到每个点了,而不需要建很多的边


代码

#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;

const int INF = 2e9;
const int MAXM = 3e5;
int N, M;
struct node // 存点 
{
	int X, Y;
	int Pos;
	node () { X = Y = 0; }
}S, T, P[MAXM + 5];
struct edge // 存图 链式前向星 
{
	int to, dis, next;
	edge () { to = dis = next = 0; }
	edge (int tt, int dd, int nn) { to = tt, dis = dd, next = nn; }
	friend bool operator < (edge a, edge b) { return a.dis > b.dis; }
}G[MAXM * 6 + 5];
int head[MAXM + 5], cnt;
int Dis[MAXM + 5];
bool vis[MAXM + 5];

int Fabs(int x) { return x > 0 ? x : -x; } // 绝对值 
int Min(int a, int b) { return a < b ? a : b; } // 最小值 

void Read(int &n) // 读入优化 
{
	n = 0;
	bool f = 1;
	char C = getchar();
	while (C < '0' || C > '9')
	{
		if (C == '-') f ^= 1;
		C = getchar();
	}
	while ('0' <= C && C <= '9')
	{
		n = (n << 3) + (n << 1) + (C ^ 48);
		C = getchar();
	}
	if (!f) n = -n;
}

bool cmpx(node a, node b) { return a.X < b.X; } // 按照横坐标排序 
bool cmpy(node a, node b) { return a.Y < b.Y; } // 按照纵坐标排序 

void addEdge(int u, int v, int w) // 建边
{
	G[++cnt] = edge(v, w, head[u]);
	head[u] = cnt;
}

int Dijkstra(int s, int t) // Dijkstra 最短路算法 
{
	for (int i = 0; i <= M + 1; i++)
		Dis[i] = INF, vis[i] = 0;
	Dis[s] = 0;
	priority_queue<edge> q;
	q.push(edge(s, 0, 0));
	while (!q.empty())
	{
		int u = q.top().to; q.pop();
		if (vis[u]) continue;
		vis[u] = 1;
		for (int i = head[u]; i > 0; i = G[i].next)
		{
			int v = G[i].to, w = G[i].dis;
			if (Dis[u] + w < Dis[v]) {
				Dis[v] = Dis[u] + w;
				q.push(edge(v, Dis[v], 0));
			}
		}
	}
	return Dis[t];
}

int main()
{
	freopen("home.in", "r", stdin);
	freopen("home.out", "w", stdout);
	Read(N), Read(M);
	Read(S.X), Read(S.Y), Read(T.X), Read(T.Y);
	P[0] = S, P[M + 1] = T;
	addEdge(0, M + 1, Fabs(S.X - T.X) + Fabs(S.Y - T.Y));
	for (int i = 1; i <= M; i++)
	{
		Read(P[i].X), Read(P[i].Y);
		P[i].Pos = i;
		addEdge(0, i, Min(Fabs(P[0].X - P[i].X), Fabs(P[0].Y - P[i].Y)));
		addEdge(i, 0, Fabs(P[i].X - P[0].X) + Fabs(P[i].Y - P[0].Y));
		addEdge(i, M + 1, Fabs(P[i].X - P[M + 1].X) + Fabs(P[i].Y - P[M + 1].Y));
		addEdge(M + 1, i, Min(Fabs(P[M + 1].X - P[i].X), Fabs(P[M + 1].Y - P[i].Y)));
	}
	sort(P + 1, P + M + 1, cmpx);
	for (int i = 1; i < M; i++)
	{
		addEdge(P[i].Pos, P[i + 1].Pos, Fabs(P[i].X - P[i + 1].X));
		addEdge(P[i + 1].Pos, P[i].Pos, Fabs(P[i + 1].X - P[i].X));
	}
	sort(P + 1, P + M + 1, cmpy);
	for (int i = 1; i < M; i++)
	{
		addEdge(P[i].Pos, P[i + 1].Pos, Fabs(P[i].Y - P[i + 1].Y));
		addEdge(P[i + 1].Pos, P[i].Pos, Fabs(P[i + 1].Y - P[i].Y));
	}
	printf("%d\n", Dijkstra(0, M + 1));
	return 0;
}


评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符 “速评一下”
©️2020 CSDN 皮肤主题: 创作都市 设计师:CSDN官方博客 返回首页