solution-p3831

题解 P3831 【[SHOI2012]回家的路】

原题

这是我们学校膜你赛上的一道题。

感觉并没有紫题难度?

思路

一眼最短路,发现\(m \le 100000\),如果枚举每两个点的话会爆掉。

那么换一种建边方式罢。

显然两个点可以互相到达,当且仅当它们的x坐标相同或y坐标相同

于是我们用一个vector记录相同x上的点,枚举每两个点,连一条边。

当然也用一个vector记录相同y上的点,枚举每两个点,连一条边。

这里我们在连边的时候把换乘要用的1min也给算到边权里,当然到终点是不用换乘的,所以最终答案要减1。

最后对起点跑一遍dijkstra,直接输出即可。

代码

// 此处应有头文件 
#define pr_q priority_queue
const int N = 2e6 + 10;
const int inf = 0x3fffffff;
int n,m,cnt,s = 0;
int head[N];
int dis[N];
bool vis[N];
struct edge
{
	int v,w,nxt;
}edge[N];
struct node
{
	int x,y;
}a[N];
void add(int u , int v , int w) // 前向星 
{
	++cnt;
	edge[cnt].v = v;
	edge[cnt].w = w;
	edge[cnt].nxt = head[u];
	head[u] = cnt;
	return ;
}
int sx,sy,fx,fy;
vector<int> samex[N],samey[N]; // samex[i]存下了所有x坐标为i的点。samey同理
struct prq // 用于dijkstra的优先队列。 
{
	int num,dis;
	bool operator < (const prq &x) const
		{return x.dis < dis;}
};
void dijkstra() // 朴素dijkstra 
{
	for(int i = 1;i <= m;i++)
		dis[i] = inf;
	dis[s] = 0;
	pr_q<prq> q;
	q.push( (prq){s , 0} );
	while( !q.empty() )
	{
		int u = q.top().num;
		q.pop();
		for(int i = head[u];i;i = edge[i].nxt)
		{
			int v = edge[i].v;
			if(dis[v] > dis[u] + edge[i].w)
			{
				dis[v] = dis[u] + edge[i].w;
				if( !vis[v] )
				{
					vis[v] = true;
					q.push( (prq){ v , dis[v] } );
				}
			}
		}
	}
}
int main()
{
	cin >> n >> m;
	for(int i = 1;i <= m;i++)
	{
		cin >> a[i].x >> a[i].y;
		samex[a[i].x].push_back(i);
		samey[a[i].y].push_back(i);
	}
	cin >> sx >> sy >> fx >> fy;
	a[0] = (node){sx , sy}; // 起点 
	samex[sx].push_back(0);
	samey[sy].push_back(0);
	a[++m] = (node){fx , fy}; // 终点 
	samex[fx].push_back(m);
	samey[fy].push_back(m);
	// 建边 
	for(int x = 1;x <= n;x++)
		for(int i = 0;i < samex[x].size();i++)
			for(int j = i + 1;j < samex[x].size();j++)
			{
				int u = samex[x][i];
				int v = samex[x][j];
				add(u , v , abs(a[u].y - a[v].y) * 2 + 1); // 同时把换乘的时间算上 
				add(v , u , abs(a[u].y - a[v].y) * 2 + 1);
			}
	for(int y = 1;y <= n;y++)
		for(int i = 0;i < samey[y].size();i++)
			for(int j = i + 1;j < samey[y].size();j++)
			{
				int u = samey[y][i];
				int v = samey[y][j];
				add(u , v , abs(a[u].x - a[v].x) * 2 + 1);
				add(v , u , abs(a[u].x - a[v].x) * 2 + 1);
			}
	dijkstra();
	if(dis[m] == inf) // 无法到达 
		puts("-1");
	else
		cout << dis[m] - 1 << endl; // 最后减1,终点不需要花时间换乘 
    return 0;
}
posted @ 2024-03-06 16:55  iorit  阅读(17)  评论(0)    收藏  举报