[洛谷luogu] P1979 [NOIP2013T6]华容道

2 篇文章 0 订阅
1 篇文章 0 订阅
60分做法:bfs四维大暴搜    O(q(nm^2))。

空白块
初始位置的块:指定块
目标位置的块:目标块

f[a][b][c][d]   a b 表指定块  c d表空白块

题解做法:

我们要把指定块通过空白块移动,当且仅当空白块在指定块相邻的四个位置。 假设要把指定块向右移动一格,那么如果空白块在不同四个位置,指定块移动到右边所需的步数不同。

当移动一个块之后,可以发现 空白块一定到了此块移动前的位置。 如果我们能表示出此块位置以及空白块在其相邻的某个方向,我们就能把每一次移动的状态记下来 这样  当此块通过移动到其相邻位置 我们可以通过先前的预处理,再从当前状态(指定块位置及相邻的空白块方位)进行移动。


可以先预处理一下
先用BFS记录下空白块移动到指定块相邻四个位置的各个步数。
把每一个可移动的块 当空白块在其上下左右四个相邻位置时 此块移动到相邻位置所需的步数记下来。
BFS记录的是什么?
走一遍样例,我们可以发现,当且仅当空白格在指定格旁边时,才可以移动。每当指定格移动一次,可以发现,图上的状态总是“一个指定格加上旁边空白格”这一整个状态的变化,因此,BFS就是把这些状态变成一个个点,然后拉一个链,存上距离。

如果空白块在指定块上方 ,我们想把指定块下移。最优做法是把空白块移到指定块下方,(BFS记录一下把空白块从上移到下的步数记录下来)
再通过一步移动即可(这一步也是改变了相对位置 也会记录下来 )。

BFS时也就把这些空白块与指定块的相对位置以及其想进行各个移动时的步数记录下来。

怎么记录其相对位置呢?

我们把这些指定块与相邻空白块的相对位置 变成一个个点  就构建了一个图 

然而我们想把指定块(设为x1,y1)在相邻空白块的条件下移动到其各个相邻位置的步数是不同的。(空白块的方位不同)

所以对每次询问    

再一次BFS 把空白块移到了指定块四个相邻位置    跑一次SPFA就可以了。   时间复杂度(O(qknm))。

PS.注意指定块与目标块重合的情况
SPFA类似滚动的数组 比队列快。。。

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
#define len 4000
int ans=16843009;
int h,t,n,m,q,vis[41][41],dis[41][41],d[1111][3];
int dd[5000];
int f1[10]={-1,1,0,0},f2[10]={0,0,-1,1};
int dist[22111];
bool map[41][41];
int e[21111][5];
int tot,cur[21111]; 
int ex,ey,sx,sy,tx,ty;
int vs[21111];
void add(int u,int v,int val)
{
    e[++tot][0]=v,e[tot][1]=val,e[tot][2]=cur[u],cur[u]=tot;
}
void bfs(int ax,int ay,int bx,int by,int fx)
{
    memset(vis,0,sizeof(vis));
    memset(dis,1,sizeof(dis));
    d[1][1]=ax,d[1][2]=ay;
    dis[ax][ay]=0;
    vis[ax][ay]=1;
    h=t=1;
    while(h<=t)
    {
        int nx=d[h][1],ny=d[h][2];
        for(int i=0;i<4;i++)
        {
            int xx=f1[i]+nx,yy=f2[i]+ny;
            if(vis[xx][yy]) continue;
            if((xx==bx&&yy==by)||!map[xx][yy]) continue;
            d[++t][1]=xx;d[t][2]=yy;
            vis[xx][yy]=1;
            dis[xx][yy]=dis[nx][ny]+1;
            //cout<<dis[xx][yy]<<' '<<xx<<' '<<yy<<" dfdfdfdfdfdfd"<<endl;
        }
        h++;
    }
/*  cout<<endl<<"~~~~~~~~~~~~"<<endl;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
            cout<<dis[i][j]<<' ';
        cout<<endl;
    }
    cout<<endl<<"~~~~~~~~~~~~"<<endl;*/
    if(fx==4) return ;
    for(int i=0;i<4;i++)
    {
        int xx=bx+f1[i],yy=by+f2[i];
        if((xx==ax&&yy==ay)||dis[xx][yy]==16843009) continue; 
        add(bx*30*4+by*4+fx,bx*30*4+by*4+i,dis[xx][yy]);
    }
    add(bx*30*4+by*4+fx,ax*30*4+ay*4+(fx^1),1);
}
void spfa()
{
    memset(vs,0,sizeof(vs));
    memset(dd,0,sizeof(dd));
//  queue <int> dd;
    h=1;t=1;
    memset(dist,1,sizeof(dist));
    for(int i=0;i<4;i++)
    {
        int xx=sx+f1[i],yy=sy+f2[i];
        //cout<<dis[xx][yy]<<' '<<xx<<' '<<yy<<endl;
        if(dis[xx][yy]==16843009||!map[xx][yy]) continue;
        int xy=sx*30*4+sy*4+i;
    //  dd.push(xy);
        dd[t++]=xy;
        vs[xy]=1;
        dist[xy]=dis[xx][yy];
    //  cout<<xy<<"   xyy"<<' '<<dist[xy]<<' '<<xx<<' '<<yy<<endl;
    }
    while(h!=t)
    {
        int now=dd[h];
        //cout<<now<<"            noww"<<' '<<cur[now]<<endl;
        //dd.pop();
        for(int i=cur[now];i;i=e[i][2])
        {
            int nn=e[i][0],val=e[i][1];
        //  cout<<nn<<"     nnnnnnnnnnnnnn"<<endl;
            if(dist[nn]>dist[now]+val)
            {
                dist[nn]=dist[now]+val;
                if(!vs[nn])
                {
                    vs[nn]=1;
                    //dd.push(nn);
                    dd[t++]=nn;
                    if(t>len ) t=1;
                //cout<<dist[nn]<<' '<<nn<<endl;
                }
            }
        }
        vs[now]=0;
        h++;
        if(h>len ) h=1;
    }

}
int main()
{
    ///memset(vis,1,sizeof(vis));
    ///cout<<vis[1][1];
    cin>>n>>m>>q;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {
            cin>>map[i][j];
        }

    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
        {

            if(!map[i][j]) continue;
            if(map[i-1][j]) bfs(i-1,j,i,j,0);
            if(map[i+1][j]) bfs(i+1,j,i,j,1);
            if(map[i][j-1]) bfs(i,j-1,i,j,2);
            if(map[i][j+1]) bfs(i,j+1,i,j,3);
        //  cout<<i<<' '<<j<<endl;
        }
//  cout<<"ce shi 22222"<<endl;
    for(int ii=1;ii<=q;ii++)
    {
        ans=16843009;
        cin>>ex>>ey>>sx>>sy>>tx>>ty;
        if(sx==tx&&sy==ty) 
        {
            cout<<0<<endl;
            continue;
        }
        bfs(ex,ey,sx,sy,4);
        spfa();
        for(int i=0;i<4;i++)
        {
        //  cout<<dist[tx*30*4+ty*4+i]<<' '<<"dfdfdfdfd"<<' '<<tx*30*4+ty*4+i<<endl;
            ans=min(ans,dist[tx*30*4+ty*4+i]);
        }

        printf("%d\n",ans==16843009?-1:ans);
    }
    return 0;
}
参考:
洛谷题解[http://www.luogu.org/wiki/show?name=%E9%A2%98%E8%A7%A3+P1979](http://www.luogu.org/wiki/show?name=%E9%A2%98%E8%A7%A3%20P1979)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值