Uva1666最短路线——技巧

题意:

平面上右n个建筑物,求(x1,y1)到(x2,y2)的一条路,使得转弯次数最少。建筑物都是坐标平行于坐标轴的矩形。可以相互接触但不重叠(接触的点或边都不可通过),只能沿着平行于坐标轴的直线走,可以沿着建筑物的边走,但不可穿越建筑物。

思路:

lx,ly对应建筑的左下角,rx,ry对应右上角
首先明白一点,对于建筑A和建筑B,若A.rx < B.lx,那么A和B之间必定有路可走,和A和B之间距离多宽无关。所以我们可以给建筑的x,y分别编号,按照从小到大的顺序,x(y)小的编号就小。
题目还说两矩形公共边和公共点不可走,为方便处理,可以把每个坐标点扩展为一个3x3的区域。
如下图(画的丑了点):(1,2),(2,3)的矩形变为(3,6),(6,9)的矩形,这样的话中心浅蓝色区域不可行走,周围绿色区域可走。
在这里插入图片描述
判断两个矩形有公共边(点)时,只用判断(x,y)再走两步(8个方向)能不能走到另一个矩形,如果能,则这两个矩形有公共边(点)。
map默认按照key升序排序,所以省去了排序。

#include <cstdio>
#include <queue>
#include <map>
#include <cstring>
#include <algorithm>
#define fi first
#define se second
#define pii pair<int,int>
using namespace std;
const int INF = 0x3f3f3f3f;
typedef long long LL;
const int maxn = 50+5;
const int maxw = 300+5;

int d1[4][2] = {{0,1}, {1,0}, {0,-1}, {-1,0}};		// 上下左右 
int d2[4][2] = {{1,1}, {1,-1}, {-1,-1}, {-1,1}};	// 对角线 
int G[maxw][maxw];
map<int,int> RX, RY;

// 标号 
void label(map<int, int> &X){
	int cnt = 1;
	for(map<int,int>::iterator it = X.begin(); it != X.end(); ++it)
		it->se = cnt++;
}

// 在矩形区域内部填充 1,表示禁止通过。lx,ly对应左下角,rx,ry对应右上角 
void fillmap(int lx, int ly, int rx, int ry, int val){
	int x1, x2, y1, y2;
	int a = 3*RX[lx] + 1, b = 3*RX[rx] - 1;
	int c = 3*RY[ly] + 1, d = 3*RY[ry] - 1;
	for(int i = a; i <= b; ++i){
		for(int j = c; j <= d; ++j){
			G[i][j] = val;
			// 判断临边和相连的点
			for(int k = 0; k < 4; ++k){
				// 检查相邻的上下左右 
				x1 = i + 2*d1[k][0], y1 = j + 2*d1[k][1];
				x2 = i + d1[k][0],   y2 = j + d1[k][1];
				if(G[x1][y1] == val) G[x2][y2] = val;
				// 检查四个角
				x1 = i + 2*d2[k][0], y1 = j + 2*d2[k][1];
				x2 = i + d2[k][0],   y2 = j + d2[k][1];
				if(G[x1][y1] == val) G[x2][y2] = val;
			} 
		}
	}
}

pii fillPtmap(int lx, int ly, int val){
	int x = RX[lx] * 3, y = RY[ly] * 3;
	G[x][y] = val;
	return make_pair(x, y);
}

int dis[maxw][maxw][4];
void bfs(int sx, int sy, int ex, int ey){
	pii st, ed;	// 起点和终点在地图中的坐标 
	st = fillPtmap(sx, sy, 2);
	ed = fillPtmap(ex, ey, 3);
	//printf("s: %d,%d; end: %d,%d\n",st.fi, st.se, ed.fi, ed.se);
	queue<int> X, Y, D;
	memset(dis, 0x3f, sizeof(dis));
	for(int i = 0; i < 4; ++i){
		X.push(st.fi); Y.push(st.se); D.push(i);
		dis[st.fi][st.se][i] = 0;
	}
	int ans = INF;
	while(!X.empty()){
		int x = X.front(); X.pop();
		int y = Y.front(); Y.pop();
		int d = D.front(); D.pop();
		if(dis[x][y][d] >= ans) continue;
		for(int i = 0; i < 4; ++i){
			int turn = (d != i);
			int tx = x + d1[i][0], ty = y + d1[i][1];
			if(tx < 0||tx >= maxw||ty < 0||ty >= maxw||1 == G[tx][ty]) continue;
			if(dis[tx][ty][i] > dis[x][y][d] + turn){
				dis[tx][ty][i] = dis[x][y][d] + turn;
				if(tx == ed.fi&&ty == ed.se){
					ans = min(ans, dis[tx][ty][i]);
					continue;
				}
				X.push(tx); Y.push(ty); D.push(i);
			}
		}
	}
	printf("%d\n", ans == INF ? -1 : ans);
}

int main()
{
	//freopen("in.txt","r",stdin);
	int n, sx, sy, ex, ey, N;
    int lx[maxn], ly[maxn], rx[maxn], ry[maxn];
	while(scanf("%d %d %d %d", &sx, &sy, &ex, &ey) == 4){
		if (sx == 0 && sy == 0 && ex == 0 && ey == 0) return 0;
		scanf("%d",&n);
		RX.clear(); RY.clear();
		for(int i = 0; i < n; ++i){
			scanf("%d%d%d%d",&lx[i],&ly[i],&rx[i],&ry[i]);
			// 强行令lx,ly对应左下角,rx,ry对应右上角
			if(lx[i] > rx[i]) swap(lx[i], rx[i]);
			if(ly[i] > ry[i]) swap(ly[i], ry[i]);
			RX[lx[i]] = RX[rx[i]] = 1;
			RY[ly[i]] = RY[ry[i]] = 1;
		}
		RX[sx] = RX[ex] = 1;
		RY[sy] = RY[ey] = 1;
		
		label(RX); label(RY);
//		for(map<int,int>::iterator it = RX.begin(); it != RX.end(); ++it){
//			printf("%d,%d  ",it->fi, it->se);
//		}puts("");
		
		memset(G, 0, sizeof(G));
		for(int i = 0; i < n; ++i) fillmap(lx[i], ly[i], rx[i], ry[i], 1);
		
		bfs(sx, sy, ex, ey);
//        for (int i = 0; i < 20; i++) {
//            for (int j = 0; j < 20; j++) {
//                printf("%d", G[i][j]);
//            }
//            puts("");
//        }
	}

	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值