题目描述很详细了,不多说了。
今天因为很粗心的bug肝到了这么晚,真的惨。
这道题一开始想知道是状态当点,也知道是广搜,但是显然直接广搜会T。想不出来,忍不住查阅了一下题解(天哪我怎么这么菜),发现其实就是把所有绝招状态点都塞入队列一起广搜,然后记录每个绝招状态点的邻域(指的是某个区域,区域中点离该绝招状态点最近),这样的话,跨过邻域的边就可以用来更新答案了,也就是说两个绝招状态之间的距离一定是经过这些跨过邻域的边的,所有相邻绝招状态的距离最小值就是我们要的答案了。这个其实有点像“双向广搜”,两头一起搜找到对接点,或者可以起个名字叫“多向广搜”了,其实这个技巧以前用过不少,现在居然生疏了。
这么做的效率是多少呢?扩展过程中,假设当前状态点为(f1,f2)那么与其有相连边的就是所有(f3,f4)其中点f3与f1相连,点f4与f2相连(在题目中边的意义下)。总状态nn,边一共mm级别,广搜过程中每条边只访问两遍,所以总效率是mm+nn级别的。
现在自己的水平,离明年拿金牌还很远,需要加强训练呀! 加油 Max,加油Alchemist!
#include<cstdio>
#include<vector>
#include<algorithm>
#define x first
#define y second
using namespace std;
int n,m,k,dmin,dmax,zone[1005][1005],stp[1005][1005],ans[1000005];
pair<int,int> Q[1000005],p[1005];
vector<int> L[1005],R[1005];
int h,t;
inline void expand(int ox, int oy, int nx, int ny)
{
int tmp=abs(p[nx].x-p[ny].x)+abs(p[nx].y-p[ny].y);
if(tmp<dmin||tmp>dmax)
return ;
if(!zone[nx][ny])
{
Q[t++]=pair<int,int>{nx,ny};
zone[nx][ny]=zone[ox][oy];
stp[nx][ny]=stp[ox][oy]+1;
}
else if(zone[nx][ny]!=zone[ox][oy])
{
ans[zone[nx][ny]]=min(ans[zone[nx][ny]],stp[ox][oy]+stp[nx][ny]+1);
ans[zone[ox][oy]]=min(ans[zone[ox][oy]],stp[ox][oy]+stp[nx][ny]+1);
}
}
int main()
{
scanf("%d%d%d%d",&n,&m,&dmin,&dmax);
for(int i=1;i<=n;i++)
{
scanf("%d%d",&p[i].x,&p[i].y);
L[i].push_back(i);
R[i].push_back(i);
}
scanf("%d",&k);
for(int i=1,v,u;i<=k;i++)
{
scanf("%d%d",&v,&u);
Q[t++]=pair<int,int>{v,u};
zone[v][u]=i;
}
for(int i=1,tx,ty,o;i<=m;i++)
{
scanf("%d%d%d",&tx,&ty,&o);
if(o)
R[tx].push_back(ty),R[ty].push_back(tx);
else
L[tx].push_back(ty),L[ty].push_back(tx);
}
for(int i=1;i<=k;i++)
ans[i]=1E9;
while(h<t)
{
for(int i=0;i<L[Q[h].x].size();i++)
for(int j=0;j<R[Q[h].y].size();j++)
expand(Q[h].x,Q[h].y,L[Q[h].x][i],R[Q[h].y][j]);
h++;
}
for(int i=1;i<=k;i++)
printf("%d\n",ans[i]==1E9?-1:ans[i]);
return 0;
}