题意:
平面上右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;
}