【NOIP2013】 华容道 bfs预处理+bfs

这一题我们考虑一个最裸的算法:

我们设dp[i][j][k][l]表示当前棋子在(i,j)且空格在(k,l)时的最小步数

然后显然随便转移一下就好了,时间复杂度为O(q(nm)2)。可以获得80分的好成绩(我自测的时候只打了这个)

我们发现这一题有一些很优秀的性质:

首先整个图是静态的,且起点,终点,出现空格的位置均非障碍物。

那么对于这个性质,我们做一些微小的预处理。

考虑到当空格与目标棋子四相邻的时候,棋子才能往空格内移动。那么整个走棋的过程可以理解为:调整空格位置-移动棋子到空格里-调整空格位置

我们设f[i][j][k][l]表示在不经过(i,j)的情况下,将空格从(i+wx[k],j+wy[k])移动到(i+wx[l],j+wy[l])的最小步数,其中wx,wy满足|wx[i]+wy[i]|=1,显然有四种组合。

对于每一个f[i][j][k][l],我们通过一次bfs去求解。

预处理f的时间复杂度显然为O(16nm)

下面考虑每一组询问:

若起点和终点的坐标相同,那么显然输出0就好了。

若起点和终点坐标不相同,我们先考虑将空格移动到起点四相邻的位置,所需要的最小步数显然可以通过一次bfs求得。

然后我们设d[i][j][k]表示当前棋子在(i,j)时,空格在(i+wx[k],j+wy[k])的最小步数。

显然随便bfs就可以进行更新了。最后的答案显然是min(d[tx][ty][])

时间复杂度O(16nm+4qnm)。跑得飞快。

 

复制代码
  1 #include<bits/stdc++.h>
  2 #define M 32
  3 #define INF 16843009
  4 using namespace std;
  5 int n,m,q,mp[M][M]={0},dis[M][M]={0},d[M][M][4]={0},f[M][M][4][4]={0};
  6 const int wx[]={0,1,0,-1};
  7 const int wy[]={1,0,-1,0};
  8 queue<int> qx,qy,qk;
  9 void bfs(int X,int Y){
 10     memset(dis,1,sizeof(dis));
 11     qx.push(X); qy.push(Y); dis[X][Y]=0;
 12     while(!qx.empty()){
 13         X=qx.front(); qx.pop();
 14         Y=qy.front(); qy.pop();
 15         for(int i=0;i<4;i++){
 16             int _x=X+wx[i],_y=Y+wy[i];
 17             if(mp[_x][_y]&&dis[_x][_y]>dis[X][Y]+1){
 18                 dis[_x][_y]=dis[X][Y]+1;
 19                 qx.push(_x); qy.push(_y);
 20             }
 21         }
 22     }
 23 }
 24 void move(int x,int y){
 25     memset(dis,1,sizeof(dis));
 26     qx.push(x); qy.push(y); dis[x][y]=0;
 27     while(!qx.empty()){
 28         x=qx.front(); qx.pop();
 29         y=qy.front(); qy.pop();
 30         for(int i=0;i<4;i++){
 31             int _x=x+wx[i],_y=y+wy[i];
 32             if(mp[_x][_y]&&dis[_x][_y]>dis[x][y]+1){
 33                 dis[_x][_y]=dis[x][y]+1;
 34                 qx.push(_x); qy.push(_y);
 35             }
 36         }
 37     }
 38 }
 39 
 40 
 41 void bfs(){
 42     while(!qx.empty()){
 43         int x=qx.front(); qx.pop();
 44         int y=qy.front(); qy.pop();
 45         int k=qk.front(); qk.pop();
 46         for(int i=0;i<4;i++){
 47             int _x=x+wx[i],_y=y+wy[i];
 48             if(mp[_x][_y]==0) continue;
 49             if(d[_x][_y][(i+2)&3]>d[x][y][k]+f[x][y][k][i]+1){
 50                 d[_x][_y][(i+2)&3]=d[x][y][k]+f[x][y][k][i]+1;
 51                 qx.push(_x); qy.push(_y); qk.push((i+2)&3);
 52             }
 53         }
 54     }
 55 }
 56             
 57 int main(){
 58     scanf("%d%d%d",&n,&m,&q);
 59     for(int i=1;i<=n;i++)
 60     for(int j=1;j<=m;j++) scanf("%d",&mp[i][j]);
 61     
 62     for(int i=1;i<=n;i++)
 63     for(int j=1;j<=m;j++)
 64     if(mp[i][j]){
 65         mp[i][j]=0;
 66         for(int k=0;k<4;k++)
 67         if(mp[i+wx[k]][j+wy[k]]){
 68             bfs(i+wx[k],j+wy[k]);
 69             for(int l=0;l<4;l++)
 70             f[i][j][k][l]=dis[i+wx[l]][j+wy[l]];
 71         }
 72         mp[i][j]=1;
 73     }
 74     
 75     while(q--){
 76         int ex,ey,sx,sy,tx,ty;
 77         cin>>ex>>ey>>sx>>sy>>tx>>ty;
 78         if(sx==tx&&sy==ty){
 79             printf("0\n");
 80             continue;
 81         }
 82         mp[sx][sy]=0;
 83         move(ex,ey);
 84         mp[sx][sy]=1;
 85         memset(d,1,sizeof(d));
 86         for(int i=0;i<4;i++)
 87         if(mp[sx+wx[i]][sy+wy[i]]){
 88             qx.push(sx);
 89             qy.push(sy);
 90             qk.push(i);
 91             d[sx][sy][i]=dis[sx+wx[i]][sy+wy[i]];
 92         }
 93         bfs();
 94         int minn=INF;
 95         for(int i=0;i<4;i++)
 96         minn=min(minn,d[tx][ty][i]);
 97         if(minn==INF) printf("-1\n");
 98         else cout<<minn<<endl;
 99     }
100 }
复制代码
posted @   AlphaInf  阅读(354)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示