题目连接:http://acm.csust.edu.cn/problem/4051
博客园食用链接:https://www.cnblogs.com/lonely-wind-/p/13941896.html
Description
父亲是最高法院的法官,母亲是国际人道救援组织职员。在父母的熏陶下,有强烈的正义感,弥子初中起便担任着学校的风纪委员。
高二的时候,弥子加入了学生会,由于学生会每天都有大量的工作要做,弥子放学后必须马上去学生会处理文件,但是她没有忘记自己风纪委员的工作,再去往学生会的路上,她会先去视察多个违纪现象高发地点中的两个,然后再前往学生会,由于时间紧迫,她必须有选择两个地点使她视察完后再去往学生会所花费的时间最小。
我们把整个秀知院看成一个 w ∗ w w * w w∗w 的二维平面图,每个位置可表示为 ( x , y ) (x, y) (x,y) , ( 1 ≤ x ≤ w , 1 ≤ y ≤ w ) (1 \leq x \leq w, 1 \leq y \leq w) (1≤x≤w,1≤y≤w) ,每个点可以向上下左右四个方向进行移动,每次移动所花费的时间是 1 1 1 ,当然秀知院有 m m m 个建筑,弥子不能穿过这些建筑,只能绕路。
总共有 n n n 个违纪现象高发地点,我们用 ( x i , y i ) (x_i, y_i) (xi,yi) 表示, ( 1 ≤ i ≤ n ) (1 \leq i \leq n) (1≤i≤n) , 保证这 n n n 个地点不会重复
接下来有 q q q 组询问, 每组询问告诉你 弥子一开始的位置位于 ( s x , s y ) (sx, sy) (sx,sy) , 学生会位于 ( e x , e y ) (ex, ey) (ex,ey) ,对于每组询问,请你求出在视察两个违纪现象高发点后再前往学生会的最小时间
Input
第一行一个整数 w w w , ( 1 ≤ w ≤ 1 e 3 ) (1 \leq w \leq 1e3) (1≤w≤1e3) 代表秀知院的二维平面的宽
接下来一个整数 n n n , ( 1 ≤ n ≤ 30 ) (1 \leq n \leq 30) (1≤n≤30) ,代表违纪现象高发点的数量
接下来 n n n 行,每行两个整数 x , y x, y x,y , ( 1 ≤ x , y ≤ w ) (1 \leq x, y \leq w) (1≤x,y≤w) ,代表每个违纪现象高发点的坐标
接下来一个整数 m m m , ( 1 ≤ m ≤ w ∗ w ) (1 \leq m \leq w * w) (1≤m≤w∗w) ,代表建筑的数量
接下来 m m m 行,每行两个整数 x , y x, y x,y , ( 1 ≤ x , y ≤ w ) (1 \leq x, y \leq w) (1≤x,y≤w) ,代表建筑的坐标
再接下来一行一个整数 q q q , ( 1 ≤ q ≤ 1 e 5 ) (1 \leq q \leq 1e5) (1≤q≤1e5) 代表询问的次数
接下来 q q q 行,每行四个整数 s x , s y , e x , e y sx, sy, ex, ey sx,sy,ex,ey , ( 1 ≤ s x , s y , e x , e y ≤ w ) (1 \leq sx, sy, ex, ey \leq w) (1≤sx,sy,ex,ey≤w) , 代表弥子一开始的位置和学生会的位置, 可能存在 s x = e x , s y = e y sx = ex, sy = ey sx=ex,sy=ey 的情况
输出 q q q 行, 每行一个整数,代表每个询问的答案,若弥子无法完成任务,则输出 − 1 -1 −1
Output
输出 q q q 行, 每行一个整数,代表每个询问的答案
Sample Input 1
5
2
4 5
3 2
2
2 2
3 5
1
5 5 4 4
Sample Output 1
8
Sample Input 2
5
1
4 1
2
1 3
5 3
2
3 3 5 5
4 4 1 4
Sample Output 2
-1
-1
emmm,需要到达两个违纪点,但违纪点的数量不是很多,只有30个,那么我们可以bfs预处理出每个违纪点到图上每个点的距离,其理论大概复杂度为 O ( 30 n 2 ) O(30n^2) O(30n2),接下来处理问题,我们可以直接枚举先到哪个点后到哪个点,其复杂度为 q m 2 qm^2 qm2其中m表示违纪点的数量。于是我们就有 a n s = m i n ( d i s [ s ] [ i ] + d i s [ i ] [ j ] + d i s [ j ] [ t ] ) ans=min(dis[s][i]+dis[i][j]+dis[j][t]) ans=min(dis[s][i]+dis[i][j]+dis[j][t])其中s为起点,i,j表示第i,j个违纪点,t为终点。那么代码也很好写。
以下是AC代码:
#include <bits/stdc++.h>
using namespace std;
const int mac=1e6+10;
const int maxn=100;
const int maxw=1e3+10;
const int inf=2e8+10;
struct node
{
int x,y;
}bug[maxn];
int dis[maxw][maxw][33];
bool vis[maxw][maxw],box[maxw][maxw];
int dx[]={0,-1,1,0},dy[]={1,0,0,-1};
inline bool ok(int x,int y,int w)
{
if (x>=1 && x<=w && y>=1 && y<=w) return true;
return false;
}
void bfs(int sx,int sy,int w,int id)
{
queue<node>q;
q.push(node{sx,sy});
memset(vis,false,sizeof vis);
dis[sx][sy][id]=0;
while (!q.empty()){
node u=q.front();
q.pop();
if (vis[u.x][u.y]) continue;
vis[u.x][u.y]=true;
for (int i=0; i<4; i++){
int x=u.x+dx[i],y=u.y+dy[i];
if (!ok(x,y,w) || vis[x][y] || box[x][y]) continue;
dis[x][y][id]=dis[u.x][u.y][id]+1;
q.push(node{x,y});
}
}
}
int main(int argc, char const *argv[])
{
int w,n,m;
scanf ("%d%d",&w,&n);
for (int i=1; i<=n; i++){
int x,y;
scanf ("%d%d",&x,&y);
bug[i]=node{x,y};
}
scanf ("%d",&m);
memset(box,false,sizeof box);
for (int i=1; i<=m; i++){
int x,y;
scanf ("%d%d",&x,&y);
box[x][y]=true;
}
for (int i=1; i<=n; i++)
for (int j=1; j<=w; j++)
for (int k=1; k<=w; k++)
dis[j][k][i]=inf;
if (n>=2){
for (int i=1; i<=n; i++)
bfs(bug[i].x,bug[i].y,w,i);
}
int q;
scanf ("%d",&q);
while (q--){
int sx,sy,ex,ey;
scanf ("%d%d%d%d",&sx,&sy,&ex,&ey);
if (n<2) {printf("-1\n"); continue;}
int ans=inf;
for (int i=1; i<=n; i++)
for (int j=1; j<=n; j++){
if (i==j) continue;
int px=bug[j].x,py=bug[j].y;
ans=min(ans,dis[sx][sy][i]+dis[ex][ey][j]+dis[px][py][i]);
}
if (ans==inf) printf("-1\n");
else printf("%d\n",ans);
}
return 0;
}