hihocoder


描述

提示:本题与“积水的城市”相比,数据范围扩大了。

如下图所示,某市市区由M条南北向的大街和N条东西向的道路组成。其中由北向南第i条路和第i+1条路之间的距离是Bi (1 <= i < N),由西向东第i条街和第i+1条街之间的距离是Ai (1 <= i < M)。

小Ho现在位于第x条路和第y条街的交叉口,他的目的地是第p条路和第q条街的交叉口。由于连日降雨,城市中有K个交叉口积水太深不能通行。小Ho想知道到达目的地的最短路径的长度是多少。

输入

第一行包含两个整数N和M。(1 <= N, M <= 1000)  

第二行包含N-1个整数, B1, B2, B3, ... BN-1。(1 <= Bi <= 100000)  

第三行包含M-1个整数, A1, A2, A3, ... AM-1。(1 <= Ai <= 100000)  

第四行包含一个整数K,表示积水的交叉口的数目。 (1 <= K <= 30)  

以下K行每行包含2个整数,X和Y,表示第X条街和第Y条路的交叉口积水。(1 <= X <= N, 1 <= Y <= M)  

第K+5行包含一个整数Q,表示询问的数目。 (1 <= Q <= 1000)  

以下Q行每行包含4个整数x, y, p, q,表示小Ho的起止点。起止点保证不在积水的交叉口处。  (1 <= x, p <= N, 1 <= y, q <= M)

输出

对于每组询问,输出最短路的长度。如果小Ho不能到达目的地,输出-1。


解题思路:可以用简单SPFA去解,明显会超时,由于积水的交叉口的数目很少,可以容易想到,图中某些点肯定不会走到,这样就对图进行压缩,横向和纵向都进行压缩,只走起点和终点并且交叉口的横纵坐标的列并且和他们相邻的列。代码如下:

#include <iostream>
#include <stdio.h>
#include <vector>
#include <map>
#include <cmath>
#include <set>
#include <queue>
#include <algorithm>
using namespace std;

#define MK make_pair

const int inf = 1000000007;
const int N = 1005;
const int M = 1005;

int n,m,block_num;
int dis_n[N],dis_m[M];
int next_l[M],next_r[M],next_l_n[N],next_r_n[N]; // 对图进行压缩

map<int,int> mp; //对图进行压缩使用的数据结构
map<int,int>mp_n;<span style="font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;">//对图进行压缩使用的数据结构</span>

set<pair<int,int> > st;  // 保存积水交叉口

int dis[N][M];//最短路径
int start_n,start_m,end_n,end_m;
int sum_m[M],sum_n[M];  //对距离求和,进行预处理

//判断是否加入队列中
void inline judge(queue<pair<int,int> >& que,int x,int y,int new_dis,int add_dis)
{
	if(1<=x && x <= n && 1 <= y && y <= m && st.find(MK(x,y)) == st.end())
	{
		if(dis[x][y] >  new_dis+ add_dis)
		{
			dis[x][y] = new_dis+ add_dis;
			if(x != end_n || y != end_m)
				que.push(MK(x,y));
		}
	}
}


int SPFA()
{
//对图进行压缩,找上下左右的邻居
	mp[start_m] += 1;
	mp[end_m] += 1;

	mp_n[start_n] += 1;
	mp_n[end_n] += 1;

	int pre_num = 0,current_num;
	map<int,int>::iterator it = mp.begin();
	it++;
	for(;it != mp.end();it++)
	{
		current_num = it->first;
		next_r[pre_num] = current_num;
		next_l[current_num] = pre_num;

		pre_num = current_num;
	}

	pre_num = 0;
	it = mp_n.begin();
	it++;
	for(;it != mp_n.end();it++)
	{
		current_num = it->first;
		next_r_n[pre_num] = current_num;
		next_l_n[current_num] = pre_num;
		pre_num = current_num;
	}


	dis[start_n][start_m] = 0;
	queue<pair<int,int> >que;
	que.push(MK(start_n,start_m));

	int x,y;
	pair<int,int> node;
	while(!que.empty())
	{
		node = que.front();
		que.pop();
		//cout << node.first << ' ' << node.second << endl;
<span style="white-space:pre">		</span>// 下面的点
		x = next_r_n[node.first];
		y = node.second;
		judge(que,x,y,dis[node.first][node.second],sum_n[x - 1] - sum_n[node.first - 1]);
<span style="white-space:pre">		</span>//上面的点
		x = next_l_n[node.first];
		y = node.second;
		judge(que,x,y,dis[node.first][node.second],sum_n[node.first - 1] - sum_n[x - 1]);
<span style="white-space:pre">		</span>//右面的点
		x = node.first;
		y = next_r[node.second];
		judge(que,x,y,dis[node.first][node.second],sum_m[y - 1] - sum_m[node.second - 1]);
<span style="white-space:pre">		</span>//左面的点
		x = node.first;
		y = next_l[node.second];
		judge(que,x,y,dis[node.first][node.second],sum_m[node.second - 1] - sum_m[y - 1]);
		
	}
<span style="white-space:pre">	</span>//删除查询点,避免查询过多的情况下,压缩图没什么效果
	mp[start_m] -= 1;
	mp[end_m] -= 1;
	if(mp[start_m] == 0)
		mp.erase(start_m);
	
	if(mp[end_m] == 0)
		mp.erase(end_m);
	
	mp_n[start_n] -= 1;
	mp_n[end_n] -= 1;
	if(mp_n[start_n] == 0)
		mp_n.erase(start_n);
	
	if(mp_n[end_n] == 0)
		mp_n.erase(end_n);
	
	if(dis[end_n][end_m] != inf)
		return dis[end_n][end_m];
	return -1;
}
int main()
{
	scanf("%d %d",&n,&m);
	sum_n[0] = 0;
	for(int i = 1;i<n;i++)
	{
		scanf("%d",dis_n + i);
		sum_n[i] = sum_n[i -1] + dis_n[i];
	}
	sum_m[0] = 0;
	for(int i = 1;i<m;i++)
	{
		scanf("%d",dis_m + i);
		sum_m[i] = sum_m[i -1] + dis_m[i];
	}

	scanf("%d",&block_num);
	int x,y;
	for(int i = 0;i<block_num;i++)
	{
		scanf("%d %d",&x,&y);
		st.insert(MK(x,y));
		mp[y] = 1;
		if(y >1)
			mp[y - 1] = 1;
		if(y < m)
			mp[y + 1] = 1;
		
		mp_n[x] = 1;
		if(x >1)
			mp_n[x - 1] = 1;
		if(x < n)
			mp_n[x + 1] = 1;
	}
<span style="white-space:pre">	</span>// 加入边界,使程序好处理
	mp[0] = 1;
	mp[m + 1] = 1;
	mp_n[0] = 1;
	mp_n[n + 1] = 1;
	int q;
	scanf("%d",&q);
	for(int i = 0;i<q;i++)
	{
		scanf("%d %d %d %d",&start_n,&start_m,&end_n,&end_m);
		if(start_m > end_m)
		{
			swap(start_n,end_n);
			swap(start_m,end_m);
		}

		for(int j = 0;j<=n;j++)
			for(int k = 0;k<=m;k++)
				dis[j][k] = inf;
		printf("%d\n",SPFA());
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值