2020 China Collegiate Programming Contest, Weihai B Labyrinth

写在前面:

这题赛时就过了二十多个人感觉还是榜歪了,完全没有到金牌题的水平,算是一道纯思维题吧,只需要简单的bfs,而且赛中也有一些铜牌区的队伍能过,所以还是要敢于开题,说不定就能写呢

题目链接 Labyrinth

题意:

给了一个n行m列的地图,有k个障碍物,q次询问每次给出两个点的坐标问两点间的最短路径

范围:

( n , m < = 200000 ) , ( n ∗ m < = 200000 ) , ( k < = 42 ) , ( q < = 100000 ) (n,m<=200000),(n*m<=200000),(k<=42),(q<=100000) (n,m<=200000),(nm<=200000),(k<=42),(q<=100000

知识点:

思维, b f s bfs bfs

题解:

求两点之间的最短距离,两种情况

1:距离为最短距离

2:距离为非最短

若结果为2,则一定经过至少一个障碍物的相邻点

如图:
在这里插入图片描述
图示起点到终点的一种最短路径经过1好点的上方

考虑求出所有障碍物相邻点x到其它所有点y的最短距离d[x][y]
情况2的结果为 m i n 1 = m i n ( m i n 1 , d [ x ] [ 起点 ] + d [ x ] [ 终点 ] ) min1=min(min1,d[x][起点]+d[x][终点]) min1=min(min1,d[x][起点]+d[x][终点])

判断起点到终点是情况1还是2即可
将起点到终点连接成一个矩形,若矩形内没有障碍物则为情况1,若矩形内有障碍物则继续分类
3、有障碍物且起点到终点距离为最短
4、有障碍物且起点到终点距离为非最短
若为情况3
如图:
在这里插入图片描述
一定有一条最短路径经过至少一个障碍物相邻点,情况3结果为 m i n 1 min1 min1

若为情况4则与情况2对应,结果为 m i n 1 min1 min1

综上所述:若起点与终点连接成的矩形内没有障碍物则结果为最短距离,若有障碍物则结果为 m i n 1 min1 min1

复杂度分析:
判断矩形内是否有障碍物用二维前缀和即可
所有障碍物相邻点跑一遍 b f s bfs bfs +每次询问时间 O ( 4 k ∗ ( n m + q ) ) O(4k*(nm+q)) O(4k(nm+q))

代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll mod=1e9+7;
const int N=2e5+5;
vector<int>w[170][200002];
vector<bool>bj[200002],bj1[200002];
vector<int>s[200002],sum[200002];
struct duqi{
	int x,y,k;
};
duqi q[N];
duqi qq[200];
duqi qq1[50];
int dx[4]={-1,0,1,0},dy[4]={0,1,0,-1};
int main()
{
	int i,j,l,n,m,t,k,l1,r,r1,mid,g,max1,i1,w2,x,h,y,v,x0,y0,p1,g1,p,min1;
	g=0;g1=1e9;
	scanf("%d%d%d%d",&n,&m,&k,&t);
	for(i=0;i<=n+1;i++){
	for(j=0;j<=m+1;j++)bj[i].push_back(g),s[i].push_back(g),bj1[i].push_back(g),sum[i].push_back(g);
    }
    p=0;p1=0;
	for(i=1;i<=k;i++){
		scanf("%d%d",&x,&y);qq1[++p1].x=x,qq1[p1].y=y;
		s[x][y]=1;
	}
	for(i=1;i<=k;i++)
	for(j=0;j<4;j++){
			x0=qq1[i].x+dx[j];y0=qq1[i].y+dy[j];
			if(bj[x0][y0]==0&&x0>=1&&x0<=n&&y0>=1&&y0<=m&&s[x0][y0]==0)qq[++p].x=x0,qq[p].y=y0,bj[x0][y0]=1;
		}
		
		for(i1=1;i1<=p;i1++)
		for(i=0;i<=n+1;i++)
		for(j=0;j<=m+1;j++)w[i1][i].push_back(g1);
		
		for(i1=1;i1<=p;i1++){
			for(i=1;i<=n;i++)
			for(j=1;j<=m;j++)bj1[i][j]=0;
			q[1]=qq[i1];q[1].k=0;bj1[qq[i1].x][qq[i1].y]=1;
			for(i=1,j=1;i<=j;i++){
				w[i1][q[i].x][q[i].y]=q[i].k;
				for(l=0;l<4;l++){
					x0=q[i].x+dx[l];y0=q[i].y+dy[l];
					if(bj1[x0][y0]==0&&x0>=1&&x0<=n&&y0>=1&&y0<=m&&s[x0][y0]==0){
						q[++j].x=x0;q[j].y=y0;q[j].k=q[i].k+1;bj1[x0][y0]=1;
					}
				}
			}
		}
		
		for(i=1;i<=n;i++)
		for(j=1;j<=m;j++)sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+s[i][j];
		
		while(t--){
			scanf("%d%d%d%d",&x,&y,&x0,&y0);
			min1=1e9;
			for(i=1;i<=p;i++){
			min1=min(min1,w[i][x][y]+w[i][x0][y0]);
		}
			k=max(x,x0);x=min(x,x0);x0=k;
			k=max(y,y0);y=min(y,y0);y0=k;
			if(sum[x0][y0]-sum[x0][y-1]-sum[x-1][y0]+sum[x-1][y-1]==0)printf("%d\n",x0-x+y0-y);
			else
			if(min1==1e9)printf("-1\n");
			else
			printf("%d\n",min1);
		}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值