Luogu 1979 NOIP 2013 华容道(搜索,最短路径)

Description

小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次。于是,他想到用编程来完成华容道:给定一种局面, 华容道是否根本就无法完成,如果能完成, 最少需要多少时间。

小 B 玩的华容道与经典的华容道游戏略有不同,游戏规则是这样的:

在一个 nm 棋盘上有 nm 个格子,其中有且只有一个格子是空白的,其余 nm-1个格子上每个格子上有一个棋子,每个棋子的大小都是 11 的;

有些棋子是固定的,有些棋子则是可以移动的;

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

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

给定一个棋盘,游戏可以玩 q 次,当然,每次棋盘上固定的格子是不会变的, 但是棋盘上空白的格子的初始位置、 指定的可移动的棋子的初始位置和目标位置却可能不同。第 i 次

玩的时候, 空白的格子在第 EXi 行第 EYi列,指定的可移动棋子的初始位置为第 SXi 行第 SYi列,目标位置为第 TXi 行第 TYi 列。

假设小 B 每秒钟能进行一次移动棋子的操作,而其他操作的时间都可以忽略不计。请你告诉小 B 每一次游戏所需要的最少时间,或者告诉他不可能完成游戏。

Input

第一行有 3 个整数,每两个整数之间用一个空格隔开,依次表示 n、m 和 q;

接下来的 n 行描述一个 n*m 的棋盘,每行有 m 个整数,每两个整数之间用一个空格隔开,每个整数描述棋盘上一个格子的状态,0 表示该格子上的棋子是固定的,1 表示该格子上的棋子可以移动或者该格子是空白的。接下来的 q 行,每行包含 6 个整数依次是 EXi、EYi、SXi、SYi、TXi、TYi,每两个整数之间用一个空格隔开,表示每次游戏空白格子的位置,指定棋子的初始位置和目标位置。

Output

输出有 q 行,每行包含 1 个整数,表示每次游戏所需要的最少时间,如果某次游戏无法完成目标则输出−1。

Sample Input

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

Sample Output

2

-1

Http

Luogu:https://www.luogu.org/problem/show?pid=1979

Source

搜索,最短路径

解决思路

首先考虑搜索。我们发现每一次,我们移动空格,同时可能移动我们指定的棋子,那么一直这么移,直到将指定棋子移动到目标格。这样做能获得70~90分。

然后我们想如何简化。贪心的想一想,首先肯定先得让空格移动到指定的棋子旁边,因为这样才能让指定的棋子移动。其次,当空格移动到指定的棋子旁边后,我们最好不让它再离开指定的棋子,除非是要让开一个位置或是其他的特殊操作,但最后总还是要回到指定棋子旁边。

这时我们发现,我们把原来搜索时一步移动空格一格变成了一步让空格从指定格的某一方向变到另一方向。就像下面这样



所以我们可以预处理出这个东西,即\(Skip[i][j][f1][f2]\),代表的是格子\([i][j]\)时,空格在方向\(f1\),要把空格移到方向\(f2\)并与指定格交换的步数,相当于先把空格移到指定的方向的步数再+1。

为了方便叙述,我们规定:1上,2下,3左,4右

这个怎么求呢?不用想什么复杂的算法,4重循环枚举\(Skip\)的四个参数,然后直接从\(f1\)方向上的那个格子开始\(bfs\),最后返回\(f2\)方向上格子的步数。但需要注意的是,我们在\(bfs\)的时候要把指定格(即蓝色的格子)固定下来,因为不能让他和空格交换,导致混乱,所以我们可以把指定格置为0(即与不能走的格子一样),\(bfs\)完后再置回来,这样就可以保证指定格不会在移动空格的时候动,只要最后再把空格和指定格交换即可。

这时我们再来看,我们把题目转换成了一个类似于图论的东西,图中的点就是\((i,j,f)\)其中\(f\)代表方向,\((i,j)\)代表原矩阵中的第\(i\)行第\(j\)列,而每一个\(Skip[i][j][f1][f2]\)则代表的是一条边,从\((i,j,f1)\)到\((i,j,f3)\),其中\(f3\)代表\(f2\)的反方向。

为什么是反方向呢?因为



所以我们最后在这张图上进行一次最短路算法,这里选用比较好实现的SPFA。

总结一下,这道题的流程就是:

1.输入数据,并预处理出\(Skip[i][j][f1][f2]\)

2.对于每一组询问:

(1).先让空白格移动到指定格的初始位置的周围四个格子

(2).将空白格与指定格捆绑行动,求解类最短路

另外需要注意的是,可能出现初始位置与目标位置一致的情况,这时直接输出0。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std; #define mem(Arr,x) memset(Arr,x,sizeof(Arr)) const int maxMat=40;
const int maxF=5;
const int meminf=2139062143;//这个值与memset(Arr,127,sizeof(Arr))中Arr的每一个数的值一致
const int ff[5]={0,2,1,4,3};//ff[i]表示i的相反方向 class Pos//位置
{
public:
int x,y;
Pos()
{
return;
}
Pos(int a,int b)
{
x=a;
y=b;
}
}; class Queue_Data
{
public:
int x,y,f;
}; int n,m;
int Mat[maxMat][maxMat];
Pos F[maxF];
int Skip[maxMat][maxMat][maxF][maxF];
int Dist[maxMat][maxMat][maxF];//求解最短距离
bool vis[maxMat][maxMat][maxF];
queue<Queue_Data> Q; Pos operator + (Pos A,Pos B);
Queue_Data operator + (Queue_Data A,Pos B);
void init();
int Bfs(Pos st,Pos ed);//从位置st走到位置ed的步数 int main()
{
int qus;//询问个数
scanf("%d%d%d",&n,&m,&qus);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
scanf("%d",&Mat[i][j]);
init();//初始化Skip
while (qus--)//处理询问
{
int Epx,Epy,Stx,Sty,Glx,Gly;
scanf("%d%d%d%d%d%d",&Epx,&Epy,&Stx,&Sty,&Glx,&Gly);
if ((Mat[Stx][Sty]==0)||(Mat[Glx][Gly]==0))//当初始或目标位置本身不可走时,直接输出-1
{
printf("-1\n");
continue;
}
if ((Stx==Glx)&&(Sty==Gly))//当初始与目标位置相同时,直接输出0
{
printf("0\n");
continue;
} while (!Q.empty())//初始化
Q.pop();
mem(Dist,127);
mem(vis,0); //求出将空白格移动到指定格初始位置四周的步数,并将其中可行的放入队列
Mat[Stx][Sty]=0;//因为要求出空白格移动的步数,所以这时初始位置不能动,先置为0表示不可走
Pos init=(Pos){Stx,Sty};
for (int i=1;i<=4;i++)
{
Pos v=init+F[i];//枚举初始位置的四周
Dist[Stx][Sty][i]=Bfs((Pos){Epx,Epy},v);//Bfs求解从空格走到的距离
if (Dist[Stx][Sty][i]!=meminf)
Q.push((Queue_Data){Stx,Sty,i});//如果可行,放入队列
}
Mat[Stx][Sty]=1;//处理完后重新置为1 //求解最短路
while (!Q.empty())
{
Queue_Data u=Q.front();
Q.pop();
vis[u.x][u.y][u.f]=0;
for (int i=1;i<=4;i++)//枚举四个方向
{
Queue_Data v=u+F[i];
v.f=ff[i];//注意这里最后的空格方向要变为方向i的反方向
if (Dist[v.x][v.y][v.f]>Dist[u.x][u.y][u.f]+Skip[u.x][u.y][u.f][i])
{
Dist[v.x][v.y][v.f]=Dist[u.x][u.y][u.f]+Skip[u.x][u.y][u.f][i];
if (vis[v.x][v.y][v.f]==0)
{
Q.push(v);
vis[v.x][v.y][v.f]=1;
}
}
}
} int Ans=meminf;
for (int i=1;i<=4;i++)
Ans=min(Ans,Dist[Glx][Gly][i]);//找出最小值
if (Ans==meminf)
Ans=-1;
printf("%d\n",Ans);
}
return 0;
} Pos operator + (Pos A,Pos B)//为了方便向四周移动,重载位置的加法
{
return (Pos){A.x+B.x,A.y+B.y};
} Queue_Data operator + (Queue_Data A,Pos B)//同上
{
return (Queue_Data){A.x+B.x,A.y+B.y,A.f};
} void init()
{
F[1]=(Pos){-1,0};//这个F就是在枚举向哪个方向移动时用的
F[2]=(Pos){1,0};
F[3]=(Pos){0,-1};
F[4]=(Pos){0,1};
mem(Skip,127); for (int i=1;i<=n;i++)//四重循环枚举(i,j),空格所在方向f1,要将(i,j)移动去的方向f2
for (int j=1;j<=m;j++)
{
if (Mat[i][j]==0)//若(i,j)本身不可走则不进行操作
continue;
Mat[i][j]=0;//因为不能在移动空格的时候使(i,j)被移动,所以先置为不能走
Pos now(i,j);
for (int f1=1;f1<=4;f1++)//枚举方向
for (int f2=1;f2<=4;f2++)
{
if (f1>f2)//空格在f1,当前格走到f2和空格在f2,当前格走到f1的步数是一样的
{
Skip[i][j][f1][f2]=Skip[i][j][f2][f1];
continue;
}
Skip[i][j][f1][f2]=Bfs(now+F[f1],now+F[f2])+1;//Bfs求解
}
Mat[i][j]=1;//置回来
}
return;
} int Bfs(Pos st,Pos ed)//求解从st走到ed的步数
{
if ((Mat[st.x][st.y]==0)||(Mat[ed.x][ed.y]==0))//当这两个点中有任意一个不可走时,直接返回无穷大
return meminf; //各种初始化
queue<Pos> Q;
while (!Q.empty())
Q.pop();
bool vis[maxMat][maxMat];
int Dist[maxMat][maxMat];
mem(vis,0);
mem(Dist,127); //将st放入队列
Q.push(st);
vis[st.x][st.y]=1;
Dist[st.x][st.y]=0; do
{
Pos u=Q.front();
Q.pop();
for (int i=1;i<=4;i++)//枚举向四个方向走
{
Pos v=u+F[i];
if ((Mat[v.x][v.y]==0)||(vis[v.x][v.y]==1))
continue;
vis[v.x][v.y]=1;
Dist[v.x][v.y]=Dist[u.x][u.y]+1;
Q.push(v);
}
}
while (!Q.empty());
return Dist[ed.x][ed.y];//返回步数
}

Luogu 1979 NOIP 2013 华容道(搜索,最短路径)的更多相关文章

  1. 洛谷 P1979 [ NOIP 2013 ] 华容道 —— bfs + 最短路

    题目:https://www.luogu.org/problemnew/show/P1979 真是一道好题... 首先考虑暴力做法,应该是设 f[i][j][x][y] 记录指定棋子和空格的位置,然后 ...

  2. noip 2013 华容道

    /*双向bfs (得分和单项的一样多....)70*/ #include<iostream> #include<cstdio> #include<cstring> ...

  3. Luogu 2668 NOIP 2015 斗地主(搜索,动态规划)

    Luogu 2668 NOIP 2015 斗地主(搜索,动态规划) Description 牛牛最近迷上了一种叫斗地主的扑克游戏.斗地主是一种使用黑桃.红心.梅花.方片的A到K加上大小王的共54张牌来 ...

  4. [Luogu 1052] noip 05 过河

    [Luogu 1052] noip 05 过河 题目描述 在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧.在桥上有一些石子,青蛙很讨厌踩在这些石子上.由于桥的长度和青蛙一次跳过的距离都是 ...

  5. SharePoint 2013 定制搜索显示模板(二)

    前言 之前一篇博客,简单的介绍了如何定制搜索显示模板,这一次,我们介绍一下如何定制搜索显示时,弹出来的那个页面,相信这个大家也都会遇到的. 1.第一部分就是搜索显示模板的部分,第二部分就是搜索项目详情 ...

  6. SharePoint 2013 定制搜索显示模板

    前言 之前我们已经介绍了一些关于搜索的相关配置,当然,用户关于搜索的要求可能是各种各样.有时候,用户会说,你们的显示结果太Low了,确实是:不过,在SharePoint中,我们可以很容易的定制搜索结果 ...

  7. NOIP 2013 货车运输【Kruskal + 树链剖分 + 线段树 】【倍增】

    NOIP 2013 货车运输[树链剖分] 树链剖分 题目描述 Description A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在 ...

  8. SharePoint 2013 开发——搜索架构及扩展

    博客地址:http://blog.csdn.net/FoxDave SharePoint 2013高度整合了搜索引擎,在一个场中只有一个搜索服务应用程序(SSA).它集成了FAST,只有一个代码库 ...

  9. SharePoint 2013 企业搜索架构示例

    博客地址:http://blog.csdn.net/FoxDave 本文参考自微软官方的Chart,我们来看一下企业中对于不同规模SharePoint搜索的场的架构是什么样的. 对于搜索场的规模, ...

随机推荐

  1. Ceph分布式存储集群-硬件选择

    在规划Ceph分布式存储集群环境的时候,对硬件的选择很重要,这关乎整个Ceph集群的性能,下面梳理到一些硬件的选择标准,可供参考: 1)CPU选择Ceph metadata server会动态的重新分 ...

  2. rrd文件及rrd文件与实际数据的对比研究。

    一,什么是rrd文件? 所 谓的“Round Robin” 其实是一种存储数据的方式,使用固定大小的空间来存储数据,并有一个指针指向最新的数据的位置.我们可以把用于存储数据的数据库的空间看成一个圆,上 ...

  3. python基础学习笔记(八)

    创建自已对象就python非常核心的概念,事实上,python被称为面向对象语言,本章会介绍如何创建对象.以及面向对象的概念:继承.封装.多态. 多态: 可对不同类的对象使用同样的操作. 封装:对外部 ...

  4. 基于SSH框架的考勤管理系统的设计与实现

    基于SSH框架的考勤管理系统的设计与实现

  5. 思甜雅---关于qq的NABCD的模型分析

    个人连接:http://www.cnblogs.com/xiaoliulang/ 关于QQ的NABCD模型 N--Need 随着电脑的普及,人们在网络上进行交流的时间越来越多,由于现有的交流工具还不是 ...

  6. Orcle安装环境及步骤

    Windows7环境下如何成功安装Oracle数据库      随着微软新一代操作系统 Windows7 的正式发行,使用 Windows7  的朋友也越来越多,很多人在 Windows7 环境下安装 ...

  7. Voltage Keepsake CodeForces - 801C (思维+二分)

    题目链接 这是一道很棒的二分题. 思路: 首先先思考什么情况下是可以无限的使用,即输出-1. 我们思考可知,如果每一秒内所有设备的用电量总和小于等于充电器每秒可以充的电,那么这一群设备就可以无限使用. ...

  8. 速读《构建之法》(Build to win)有感

    通过这两天时间,我粗读了<构建之法>这本书.老实说,对于这样四百多页的一本书,刚开始把这样的任务当作是一种负担,然而当我开始真正接触它时却被它幽默有趣的风格所深深吸引,它不同于以往学习的教 ...

  9. PHP文件下载功能实现

    客户端的浏览器通过HTTP协议可以实现文件下载: 方法一: 能提供用户下载的最简单的方法就是使用一个<a></a>标签,比如在页面中添加这么一行代码 <a href=&q ...

  10. Axios插件和loading的实现

    axios插件就是一个ajax插件 axios具有ajax的所有方法如 get post delete put等等的方法 使用时只需要引入即可 如import Axios form 'axios' 不 ...