P1979 华容道

题意:$n*m$棋盘上$n*m-1$颗棋子,有且只有一个格子为空白格子,每个棋子大小$1*1$

   有些棋子可以移动,而有些棋子固定,任何与空白的格子相邻(有公共的边)的格子上的棋子都可以移动到空白格子上。

   游戏的目的是把某个指定位置可以活动的棋子移动到目标位置。

   现给定棋盘,q个询问,每次给出起始点,目标点,白块的位置,问最少步数,如果到不了输出-1

输入输出样例

输入样例#1:  
3 4 2
0 1 1 1
0 1 1 0
0 1 0 0
3 2 1 2 2 2
1 2 2 2 3 2
输出样例#1:  
2
-1

【输入输出样例说明】

棋盘上划叉的格子是固定的,红色格子是目标位置,圆圈表示棋子,其中绿色圆圈表示目标棋子。

  1. 第一次游戏,空白格子的初始位置是(3,2)(图中空白所示),游戏的目标是将初始位置在(1,2)上的棋子(图中绿色圆圈所代表的棋子)移动到目标位置(2,2)(图中红色的格子)上。

移动过程如下:

  1. 第二次游戏,空白格子的初始位置是(1,2)(图中空白所示),游戏的目标是将初始位置在(2,2)上的棋子(图中绿色圆圈所示)移动到目标位置 (3,2)上。

要将指定块移入目标位置,必须先将空白块移入目标位置,空白块要移动到目标位置,必然是从位置(2,2)上与当前图中目标位置上的棋子交换位置,之后能与空白块交换位置的只有当前图中目标位置上的那个棋子,因此目标棋子永远无法走到它的目标位置, 游戏无法完成。

看完题:果断-----爆搜啊

      20分。。。。。。TLE  QAQ(不过貌似可以70分)

正解:

  脑补一下起始棋子移动的画面,起始棋子向终点移动一步,当且仅当空白格子与它相邻且在目标方向上

  所以,我们设dis[i][j][k][l][m]表示以i,j为起点,不经过与i,j相邻的m方向上的那个棋子,到各个点的最短路,

  考虑一下,白块与起始块交换后,起始块移动了一步,然后白块在起始块原来的位置上,

  要想让起始块再向目标前进一步,白块必须在与它相邻且在它目标方向的位置上,再与它交换才行,

  而dis数组恰好做到了这一点,找到白块不经过起始块到达起始块目标方向的块的最短路,直接统计,

  那一段有居多分支的搜索,我们直接省去了,节省了大量时间

  于是,相当于白块带着起始块满图瞎jb乱跑,跑到终点就行了QAQ

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cctype>
#include<queue>
#include<algorithm>
using namespace std;
#define int long long
#define olinr return
#define _ 0
#define love_nmr 0
#define DB double
int rx[5]={0,0,0,1,-1};    //四个方向
int ry[5]={0,1,-1,0,0};
int dis[35][35][35][35][5];  //dis[i][j][k][l][m]代表从(i,j)到(k,l)不经过(i,j)m边的那一个格子的最短路(m=上,下,左,右)   
bool mp[35][35];   //地图
bool viss[35][35];    //spfa的vis
bool cop[35][35];    //复制地图
int n;
int m;
int q;
int a,b,c,d,e,f;    //空白格子x,y    起始点x,y    终点x,y
int ans;                //答案
struct node    
{
    int x;
    int y;
    int dis;
};
struct wmy
{
    int dir;
    int x;
    int y;
    int dis;
}lv[55];
queue<node> que;
int cnt;
inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(!isdigit(ch))
    {
        if(ch=='-')
            f=-f;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        x=(x<<1)+(x<<3)+(ch^48);
        ch=getchar();
    }
    return x*f;
}
inline void put(int x)
{
    if(x<0)
    {
        x=-x;
        putchar('-');
    }
    if(x>9)
        put(x/10);
    putchar(x%10+'0');
}
inline void spfa(int x,int y,int k)
{
    while(!que.empty()) que.pop();
    dis[x][y][x][y][k]=0;
    que.push((node){x,y,0});
    while(!que.empty())
    {
        node tp=que.front();
        que.pop();
        viss[tp.x][tp.y]=false;
        for(int i=1;i<=4;i++)
        {
            int xx=tp.x+rx[i];
            int yy=tp.y+ry[i];
            if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&mp[xx][yy]&&dis[x][y][xx][yy][k]>dis[x][y][tp.x][tp.y][k]+1)
            {
                dis[x][y][xx][yy][k]=dis[x][y][tp.x][tp.y][k]+1;
                if(!viss[xx][yy])
                {
                    viss[xx][yy]=true;
                    que.push((node){xx,yy,0});
                }
            }
        }
    }
}
inline void bfs()
{
    while(!que.empty()) que.pop();
    que.push((node){a,b,0});
    cop[a][b]=0;
    while(!que.empty())
    {
        node tp=que.front();
        que.pop();
        for(int i=1;i<=4;i++)
        {
            int xx=tp.x+rx[i];
            int yy=tp.y+ry[i];
            if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&cop[xx][yy])
            {
                if(xx==c&&yy==d)
                {
                    cnt++;   //记录白块到起始块的信息
                    lv[cnt].x=tp.x;
                    lv[cnt].y=tp.y;
                    lv[cnt].dis=tp.dis;
                    lv[cnt].dir=i;
                }
                else
                {
                    cop[xx][yy]=0;
                    que.push((node){xx,yy,tp.dis+1});
                }
            }
        }
    }
}
inline void dfs(int x,int y,int lst1,int lst2,int dir,int tot)
{
    if(tot>=ans) return;
    if(x==e&&y==f)   //找到终点
    {
        ans=tot;
        return;
    }
    mp[x][y]=0;   //记为起始块已走过
    for(int i=1;i<=4;i++)
    {
        int xx=x+rx[i];
        int yy=y+ry[i];
        if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&mp[xx][yy]&&dis[lst1][lst2][xx][yy][dir]<=1e5)
            dfs(xx,yy,x,y,i,tot+dis[lst1][lst2][xx][yy][dir]+1);   //起始块向目标方向移动距离=白块从原来到现在的距离+1
    }
    mp[x][y]=1;  //回溯
 
}
signed main()
{
    n=read();
    m=read();
    q=read();
    memset(dis,1,sizeof dis);     //五位数组,赋值1已经很大了
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            mp[i][j]=read();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(mp[i][j])
                for(int k=1;k<=4;k++)     //跑spfa,处理不经过m方向的点的最短路(方法:把那个点变成无法走)
                {
                    int xx=i+rx[k];
                    int yy=j+ry[k];
                    if(xx>=1&&xx<=n&&yy>=1&&yy<=m&&mp[xx][yy])
                    {
                        mp[xx][yy]=0;
                        spfa(i,j,k);
                        mp[xx][yy]=1;
                    }
                }
    while(q--)
    {
        cnt=0;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=m;j++)
                cop[i][j]=mp[i][j];   //复制一遍进行操作
        a=read();
        b=read();
        c=read();
        d=read();
        e=read();
        f=read();
        if(c==e&&d==f)   //起点=终点
        {
            put(0);
            putchar('\n');
            continue;
        }
        bfs();   //找到初始时空白块到起始块的距离
        if(!cnt)   //空白块连起始块都到不了QAQ
        {   
            put(-1);
            putchar('\n');
            continue;
        }
        ans=0x7ffffffff;  //步数初始极大值
        for(int i=1;i<=cnt;i++)
            dfs(c,d,lv[i].x,lv[i].y,lv[i].dir,lv[i].dis);  //模拟跑路过程
        put(ans==0x7ffffffff? -1:ans);
        putchar('\n');
    }
    olinr ~~(0^_^0)+love_nmr;
}
 

猜你喜欢

转载自www.cnblogs.com/olinr/p/9552077.html