cf1301f
cf1301f
1.题目大意
给你一个\(n*m\)的矩阵,每个小格子都有一个颜色,颜色用整数表示,不超过\(k\)。每次你可以走向上下左右相邻的格子或者直接跳到某个颜色和你当前位置相同的格子上去。现在有\(q\)个问题,每个问题问你从\(r1,c1\)走到\(r2,c2\)最少需要多少时间。
2.数据范围
\(1 \leq n,m \leq 1000,1 \leq k \leq 40,1 \leq q \leq 10^5\)。
3.解法
本题的突破口在于\(k\)的范围比较小。 我们知道其实对于每个问题的答案的产生只有两种可能性,要么我们直接从起点走到终点,此时的答案是两点之间的曼哈顿距离,要么经过若干次同颜色传送。
第一种情况很简单,直接计算。
第二种情况复杂一点,我们可以假设从起点到终点中间经过了某个颜色\(i\)的传送,那么我们只要求出从起点到颜色\(i\)的最短距离\(x\)和从终点到颜色\(i\)的最短距离\(y\),那么此时我们的答案就是\(x+y+1\)。 求起点到颜色\(i\)的最短距离我们可以用\(bfs\)来做,这样的预处理的时间复杂度就是\(O(k*n*m)\)。 当然你可能会怀疑某次不需要加1,这个想法确实也是对的,但是如果这次不用加一,但是我们加一了,这样这次就一定不会成为我们的正确答案。 因为我们假定的是它至少要经过一次同颜色传送,那么我们枚举所有\(i\)的时候,一定能枚举到这个同颜色传递。
总的时间复杂度:\(O(k*n*m+k*q)\)
4.代码
#include<bits/stdc++.h>
using namespace std;
int const N=1000+10;
int const K=40+2;
short d[N][N][K];
int n,m,k,vis[K],a[N][N],cnt,h[K],Q;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
queue<int> q;
struct edge{
int t1,t2,nt;
}e[N*N];
void add(int a,int x,int y){
e[++cnt].t1=x;e[cnt].t2=y; e[cnt].nt=h[a]; h[a]=cnt;
}
void bfs(int c){
int l=0,r=-1;
for(int i=h[c];i;i=e[i].nt){
int x=e[i].t1;
int y=e[i].t2;
d[x][y][c]=0;
q.push(x);
q.push(y);
}
memset(vis,0,sizeof(vis));
while (!q.empty()){
int x=q.front(); q.pop();
int y=q.front(); q.pop();
if(!vis[a[x][y]]){
vis[a[x][y]]=1;
for(int i=h[a[x][y]];i;i=e[i].nt){
int tx=e[i].t1;
int ty=e[i].t2;
if(d[tx][ty][c]==-1){
d[tx][ty][c]=d[x][y][c]+1;
q.push(tx);
q.push(ty);
}
}
}
for(int i=0;i<4;i++){
int tx=x+dx[i];
int ty=y+dy[i];
if(tx<1 || ty<1 || tx>n || ty>m) continue;
if(d[tx][ty][c]>-1) continue;
q.push(tx);
q.push(ty);
d[tx][ty][c]=d[x][y][c]+1;
}
}
}
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++) {
scanf("%d",&a[i][j]);
add(a[i][j],i,j);
}
memset(d,-1,sizeof(d));
for(int i=1;i<=k;i++)
bfs(i);
scanf("%d",&Q);
while (Q--){
int x1,y1,x2,y2;
scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
int ans=abs(x1-x2)+abs(y1-y2);
for(int i=1;i<=k;i++)
ans=min(ans,d[x1][y1][i]+d[x2][y2][i]+1);
printf("%d\n",ans);
}
return 0;
}