Luogu 1979 NOIP 2013 华容道(搜索,最短路径)
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 华容道(搜索,最短路径)的更多相关文章
- 洛谷 P1979 [ NOIP 2013 ] 华容道 —— bfs + 最短路
题目:https://www.luogu.org/problemnew/show/P1979 真是一道好题... 首先考虑暴力做法,应该是设 f[i][j][x][y] 记录指定棋子和空格的位置,然后 ...
- noip 2013 华容道
/*双向bfs (得分和单项的一样多....)70*/ #include<iostream> #include<cstdio> #include<cstring> ...
- Luogu 2668 NOIP 2015 斗地主(搜索,动态规划)
Luogu 2668 NOIP 2015 斗地主(搜索,动态规划) Description 牛牛最近迷上了一种叫斗地主的扑克游戏.斗地主是一种使用黑桃.红心.梅花.方片的A到K加上大小王的共54张牌来 ...
- [Luogu 1052] noip 05 过河
[Luogu 1052] noip 05 过河 题目描述 在河上有一座独木桥,一只青蛙想沿着独木桥从河的一侧跳到另一侧.在桥上有一些石子,青蛙很讨厌踩在这些石子上.由于桥的长度和青蛙一次跳过的距离都是 ...
- SharePoint 2013 定制搜索显示模板(二)
前言 之前一篇博客,简单的介绍了如何定制搜索显示模板,这一次,我们介绍一下如何定制搜索显示时,弹出来的那个页面,相信这个大家也都会遇到的. 1.第一部分就是搜索显示模板的部分,第二部分就是搜索项目详情 ...
- SharePoint 2013 定制搜索显示模板
前言 之前我们已经介绍了一些关于搜索的相关配置,当然,用户关于搜索的要求可能是各种各样.有时候,用户会说,你们的显示结果太Low了,确实是:不过,在SharePoint中,我们可以很容易的定制搜索结果 ...
- NOIP 2013 货车运输【Kruskal + 树链剖分 + 线段树 】【倍增】
NOIP 2013 货车运输[树链剖分] 树链剖分 题目描述 Description A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在 ...
- SharePoint 2013 开发——搜索架构及扩展
博客地址:http://blog.csdn.net/FoxDave SharePoint 2013高度整合了搜索引擎,在一个场中只有一个搜索服务应用程序(SSA).它集成了FAST,只有一个代码库 ...
- SharePoint 2013 企业搜索架构示例
博客地址:http://blog.csdn.net/FoxDave 本文参考自微软官方的Chart,我们来看一下企业中对于不同规模SharePoint搜索的场的架构是什么样的. 对于搜索场的规模, ...
随机推荐
- 解密自动CPS变换
7.2 1 前言 我最一开始听到 CPS 变换这个词是在王垠的博客里 (请求不要喷我),就是那篇他第一次宣传他的40行代码的文章. 我当时什么都看不懂,所以没太注意,不过我也正在学程序语言方面的东西, ...
- JAVA中使用MD5加密实现密码加密
1.新建Md5.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package c ...
- 牛客网-小白月赛6-J-洋灰三角
题目链接https://www.nowcoder.com/acm/contest/136/J 这题我还是不找规律了,老老实实推吧,传说找规律也可以,我还是算了 递推式:f(n)=k*f(n-1)+p ...
- vs2015安装及初步试用
Vs2015一直都听说好用,便捷.之前用vc++6.0,总感觉界面很灰,让人编程兴趣不高,恰巧借此机会,安装一下vs2015,从编译器上体验下编程的舒心,方便.希望我不会变得太懒... 首先,我下的是 ...
- 《Linux内核设计与实现》读书笔记 5
第五章系统调用 5.1与内核通信 系统调用在用户空间进程和硬件设备间添加了一个中间层, 作用:为用户空间提供了一种硬件的抽象接口:保证了系统的稳定和安全,避免应用程序不正确使用硬件,窃取其他进程的资源 ...
- 使用git命令创建分支到团队项目
背景 在我们的团队中,我作为管理者,创建了一个叫HelloWorld的项目,大家各自在本地进行开发,将自己的工作贡献到我们的团队项目中.为了便于审核,我希望大家先将自己的贡献先放在属于自己的一个分支上 ...
- ThiNet: A Filter Level Pruning Method for Deep Neural Network Compression笔记
前言 致力于滤波器的剪枝,论文的方法不改变原始网络的结构.论文的方法是基于下一层的统计信息来进行剪枝,这是区别已有方法的. VGG-16上可以减少3.31FLOPs和16.63倍的压缩,top-5的准 ...
- log4php的使用方法与详细配置
log4php的使用 首先引入logger.php文件.log4php可以通过引入logger.php来完成自动加载的过程.文件位置如下: 日志记录器自身没有定义日志的输出目的地和格式,所以我们通常需 ...
- Python入门:数据结构的4种基本类型
数据结构:通俗点说,就是储存大量数据的容器.这里主要介绍Python的4种基本数据结构:列表.字典.元组.集合. 格式如下: 列表:list = [val1,val2,val3,val4],用中括号: ...
- python排序
排序算法概览 归并排序的 空间复杂度为O(n) 插入排序 基本思想是每次讲一个待排序的记录,按其关键字大小插入到前面已拍好的子序列中,直到全部完成. 直接插入排序 讲元素L(i)插入到有序序列L[1, ...