【NOIP2013提高组 day2】华容道

在这里插入图片描述
在这里插入图片描述

分析

这道暴搜题实在是太毒瘤了…….改了一下午加一个晚自习,终于在大佬的帮助下过了orz
只需要保存空白块指定块的状态
先bfs预处理出空白块从当前状态(与指定块相邻的上下左右的某一格子)到其他状态(与指定块相邻另外三个方向的格子)的最短移动步数与指定块交换的步数,将其保存在边集数组中
(点代表当前状态和其他状态,边代表最短步数)。

由于空白快最开始不一定在指定块周围,因此,对于每一次询问:
bfs存储空白块到指定块周围(上下左右)的最小步
最后再用spfa最短路求到目标点的最小步数

代码如下

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int Maxt = 32;
const int Maxn = 3610;
const int Maxm = Maxn * 5;
int n,m,p;
bool a[Maxt][Maxt],vis[Maxn];
int dx[4] = {-1,0,1, 0},
    dy[4] = { 0,1,0,-1};
int predis[Maxt][Maxt],dis[Maxn];

struct game {
    int next,to,w;
}e[Maxm];
int top,head[Maxn];
void add(int u,int v,int val) {
    top++;
    e[top].to=v;
    e[top].w=val;
    e[top].next=head[u];
    head[u]=top;
}

struct start {
    int x,y;
} nxt,cur;
queue<start>q;
queue<int>que;

int numbers(int i,int j) {
    j--; 
    return (i-1)*m+j<<2;
}

void bfs(int ex,int ey,int px,int py,int d) {
    int cx,cy,nx,ny; 
    memset(predis,-1,sizeof(predis));
    predis[px][py]=1;
    predis[ex][ey]=0;
    cur.x=ex,cur.y=ey;
    q.push(cur);
    while(!q.empty()) {
        cur=q.front();
        q.pop();
        cx=cur.x,cy=cur.y;
        for(int i=0; i<4; ++i) {
            nx=cur.x+dx[i],ny=cur.y+dy[i];
            if(a[nx][ny] && predis[nx][ny]==-1) {
                predis[nx][ny]=predis[cx][cy]+1;
                nxt.x=nx,nxt.y=ny;
                q.push(nxt);
            }
        }
    }
    if(d==8) return;
    int tmp=numbers(px,py);
    for(int i=0; i<4; ++i) {
        int x=px+dx[i],y=py+dy[i];
        if(predis[x][y]>0)
            add(tmp+d,tmp+i,predis[x][y]);   
    }
    
    add(tmp+d,numbers(ex,ey)+(d+2)%4,1);
}

void spfa(int sx,int sy) {
    int tmp;
    memset(dis,-1,sizeof(dis));
    for(int i=0; i<4; ++i) {
        int x=sx+dx[i],y=sy+dy[i];
        if(predis[x][y]!=-1) {
            tmp=numbers(sx,sy)+i;
            dis[tmp]=predis[x][y];
            que.push(tmp);
        }
    }
    int u;
    while(!que.empty()) {
        u=que.front();
        que.pop();
        vis[u]=false;
        for(int i=head[u]; i; i=e[i].next) {
            int v=e[i].to;
            if(dis[v]==-1 || dis[v]>dis[u]+e[i].w) {
                dis[v]=dis[u]+e[i].w;
                if(!vis[v]) {
                    vis[v]=true;
                    que.push(v);
                }
            }
        }
    }
} 

int main() {
    scanf("%d%d%d",&n,&m,&p);
    for(int i=1; i<=n; ++i)
        for(int j=1; j<=m; ++j)
            scanf("%d",&a[i][j]);
    for(int i=1; i<=n; ++i)
        for(int j=1; j<=m; ++j)
            if(a[i][j]) {
                if(a[i-1][j]) bfs(i-1,j,i,j,0); 
                if(a[i][j+1]) bfs(i,j+1,i,j,1);
                if(a[i+1][j]) bfs(i+1,j,i,j,2);
                if(a[i][j-1]) bfs(i,j-1,i,j,3);
            }
    int ex,ey,sx,sy,tx,ty,ans;
    while(p--) {
        scanf("%d%d%d%d%d%d",&ex,&ey,&sx,&sy,&tx,&ty);
        if(sx==tx && sy==ty) {
            printf("0\n");
            continue;
        }
        bfs(ex,ey,sx,sy,8);
        spfa(sx,sy);
        ans=INF;
        int tmp=numbers(tx,ty);
        for(int i=0; i<4; ++i)
            if(dis[tmp+i]!=-1) 
            ans=min(ans,dis[tmp+i]);
        if(ans==INF)
            ans=-1;
        printf("%d\n",ans); 
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值