Luogu P1979 华容道(bfs+最短路)
题意
题目描述
小B最近迷上了华容道,可是他总是要花很长的时间才能完成一次。于是,他想到用编程来完成华容道:给定一种局面,华容道是否根本就无法完成,如果能完成, 最少需要多少时间。
小B玩的华容道与经典的华容道游戏略有不同,游戏规则是这样的:
在一个\(n \times m\)棋盘上有\(n \times m\)个格子,其中有且只有一个格子是空白的,其余\(n \times (m-1)\)个格子上每个格子上有一个棋子,每个棋子的大小都是\(1 \times 1\)的;
有些棋子是固定的,有些棋子则是可以移动的;
任何与空白的格子相邻(有公共的边)的格子上的棋子都可以移动到空白格子上。
游戏的目的是把某个指定位置可以活动的棋子移动到目标位置。
给定一个棋盘,游戏可以玩\(q\)次,当然,每次棋盘上固定的格子是不会变的,但是棋盘上空白的格子的初始位置、指定的可移动的棋子的初始位置和目标位置却可能不同。第\(i\)次玩的时候,空白的格子在第\(EX_i\)行第\(EY_i\)列,指定的可移动棋子的初始位置为第\(SX_i\)行第\(SY_i\)列,目标位置为第\(TX_i\)行第\(TY_i\)列。
假设小B每秒钟能进行一次移动棋子的操作,而其他操作的时间都可以忽略不计。请你告诉小B每一次游戏所需要的最少时间,或者告诉他不可能完成游戏。
输入输出格式
输入格式:
第一行有\(3\)个整数,每两个整数之间用一个空格隔开,依次表示\(n,m,q\);
接下来的\(n\)行描述一个\(n \times m\)的棋盘,每行有\(m\)个整数,每两个整数之间用一个空格隔开,每个整数描述棋盘上一个格子的状态,\(0\)表示该格子上的棋子是固定的,\(1\)表示该格子上的棋子可以移动或者该格子是空白的。
接下来的\(q\)行,每行包含\(6\)个整数依次是\(EX_i,EY_i,SX_i,SY_i,TX_i,TY_i\),每两个整数之间用一个空格隔开,表示每次游戏空白格子的位置,指定棋子的初始位置和目标位置。
输出格式:
共\(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
输出样例:
2
-1
说明
【输入输出样例说明】
棋盘上划叉的格子是固定的,红色格子是目标位置,圆圈表示棋子,其中绿色圆圈表示目标棋子。
第一次游戏,空白格子的初始位置是\((3,2)\)(图中空白所示),游戏的目标是将初始位置在\((1,2)\)上的棋子(图中绿色圆圈所代表的棋子)移动到目标位置\((2,2)\)(图中红色的格子)上。
移动过程如下:

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

要将指定块移入目标位置,必须先将空白块移入目标位置,空白块要移动到目标位置,必然是从位置\((2,2)\)上与当前图中目标位置上的棋子交换位置,之后能与空白块交换位置的只有当前图中目标位置上的那个棋子,因此目标棋子永远无法走到它的目标位置,游戏无法完成。
【数据范围】
对于\(30 \%\)的数据,\(1 \leq n,m \leq 10,q=1\);
对于\(60 \%\)的数据,\(1 \leq n,m \leq 30,q \leq 10\);
对于\(100 \%\)的数据,\(1 \leq n,m \leq 30,q \leq 500\)。
思路
怨念--;
这显然是一道爆搜题,只需要搜索就能过了。
搜个锤子啊,要硬搜能过这题还会成为\(NOIP \ 2013\)的毒瘤题?我打的暴力只有\(25\)分,然而logeadd大佬有\(80\)分。
可不可以优化一下呢?其实这道题在\(bfs\)的过程中只需要记录空白格子所在位置以及需要移动的那颗棋子的位置就好了,所以我们不妨把每个\((sx,sy,ex,ey)\)状态看作一个结点,把他向所有可以转移到的点连边,当\((sx,sy)\)转移到\((tx,ty)\),\((ex,ey)\)转移到终点周围的某个格子时,统计答案,那么我们就可以用\(SPFA\)来写。
但是这样的\(SPFA\)边权只有\(1\),不就相当于是优秀一点的\(bfs\)吗?相当于我们没有做到任何的优化啊(虽然我依靠这个卡到了\(80\)分)。
能不能精简一点我们点的数量呢?
想想看,要是我们玩华容道的话,会怎么做?首先我们要把空格子“运”到那颗棋子(叫它钦定棋子好了)旁边,然后钦定棋子移动到空格子上,空格子再在保证钦定棋子位置不改变的情况下,移动到它的前面,继续“运”...
诶,那有效的状态不就是空格子在钦定棋子旁边的状态吗?这样,我们状态记录就可以变成\((sx,sy,k)\),\(k\)表示在钦定棋子的哪个方位。点的数量一下子就从\(n^4\)变成了\(n^2\)级别的。
具体怎么办呢?对于每一个位置,我们统计空格子不经过钦定棋子到达它的另一方位的最短路径,再建空格子与钦定棋子交换这一操作的边,就可以跑\(n^2\)级别的\(SPFA\)了。
AC代码
#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int MAXN=5005;
int n,m,q,G[35][35],dis[35][35],d[MAXN];
int cnt,top[MAXN],to[MAXN<<3],len[MAXN<<3],nex[MAXN<<3];
bool vis[MAXN];
int a[4]={-1,+0,+1,+0};
int b[4]={+0,+1,+0,-1};
inline int read()
{
int re=0;
char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
return re;
}
inline int f(int x,int y){return (x-1)*m*4+(y-1)*4;}
inline void add_edge(int x,int y,int z){to[++cnt]=y,len[cnt]=z,nex[cnt]=top[x],top[x]=cnt;}
void bfs(int ex,int ey,int sx,int sy,int z)
{
memset(dis,-1,sizeof dis);
dis[sx][sy]=1,dis[ex][ey]=0;
queue<PII>Q;
Q.push(make_pair(ex,ey));
while(!Q.empty())
{
int x=Q.front().first,y=Q.front().second;Q.pop();
for(int i=0;i<4;i++)
{
int dx=x+a[i],dy=y+b[i];
if(!G[dx][dy]||dis[dx][dy]!=-1) continue;
dis[dx][dy]=dis[x][y]+1;
Q.push(make_pair(dx,dy));
}
}
if(z==4) return ;
int id=f(sx,sy);
for(int i=0;i<4;i++)
if(dis[sx+a[i]][sy+b[i]]>0)
add_edge(id+z,id+i,dis[sx+a[i]][sy+b[i]]);
add_edge(id+z,f(ex,ey)+(z+2)%4,1);
}
void SPFA(int sx,int sy)
{
queue<int>Q;
memset(d,0x3f,sizeof d);
int id=f(sx,sy);
for(int i=0;i<4;i++)
if(dis[sx+a[i]][sy+b[i]]!=-1)
{
vis[id+i]=true;
d[id+i]=dis[sx+a[i]][sy+b[i]];
Q.push(id+i);
}
while(!Q.empty())
{
int now=Q.front();Q.pop();
vis[now]=false;
for(int i=top[now];i;i=nex[i])
if(d[to[i]]>d[now]+len[i])
{
d[to[i]]=d[now]+len[i];
if(!vis[to[i]])
{
vis[to[i]]=true;
Q.push(to[i]);
}
}
}
}
int main()
{
n=read(),m=read(),q=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
G[i][j]=read();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(G[i][j])
for(int k=0;k<4;k++)
if(G[i+a[k]][j+b[k]])
bfs(i+a[k],j+b[k],i,j,k);
while(q--)
{
int ex=read(),ey=read(),sx=read(),sy=read(),tx=read(),ty=read();
if(sx==tx&&sy==ty)
{
puts("0");
continue;
}
bfs(ex,ey,sx,sy,4);
SPFA(sx,sy);
int id=f(tx,ty);
int ans=0x3f3f3f3f;
for(int i=0;i<4;i++) ans=min(ans,d[id+i]);
printf("%d\n",ans==0x3f3f3f3f?-1:ans);
}
return 0;
}
Luogu P1979 华容道(bfs+最短路)的更多相关文章
- 洛谷 P1979 [ NOIP 2013 ] 华容道 —— bfs + 最短路
题目:https://www.luogu.org/problemnew/show/P1979 真是一道好题... 首先考虑暴力做法,应该是设 f[i][j][x][y] 记录指定棋子和空格的位置,然后 ...
- JZYZOJ1442 [noip2013]华容道 bfs 最短路 剪枝
http://172.20.6.3/Problem_Show.asp?id=1442 想到最短路的简直神了,如果我写我大概只能写一个30分的bfs. 从数据范围可以看出思路是bfs剪枝,但这里的剪枝是 ...
- luogu P1979 华容道
solution 被假hash可了半天....sadQAQ code // luogu-judger-enable-o2 #include<queue> #include<cstdi ...
- 洛谷 P1979 华容道 解题报告
P1979 华容道 题目描述 小\(B\)最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面, 华容道是否根本就无法完成,如果能完成, 最少需要多少时 ...
- 洛谷P1979 华容道(70分 暴力)
P1979 华容道 题目描述 [问题描述] 小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面, 华容道是否根本就无法完成,如果能完成, 最少 ...
- POJ 2251 Dungeon Master (BFS最短路)
三维空间里BFS最短路 #include <iostream> #include <cstdio> #include <cstring> #include < ...
- 【bzoj5049】[Lydsy九月月赛]导航系统 并查集+双向BFS最短路
题目描述 给你一张 $n$ 个点 $m$ 条边的随机图,边权为1.$k$ 次询问两点间最短路,不连通则输出-1. 输入 第一行包含3个正整数n,m,k(2<=n<=100000,1< ...
- 【bzoj1189】[HNOI2007]紧急疏散evacuate BFS最短路+动态加边网络流
题目描述 发生了火警,所有人员需要紧急疏散!假设每个房间是一个N M的矩形区域.每个格子如果是'.',那么表示这是一块空地:如果是'X',那么表示这是一面墙,如果是'D',那么表示这是一扇门,人们可以 ...
- BZOJ 1195 [HNOI2006]最短母串 (Trie图+状压+bfs最短路)
BZOJ1195 LOJ10061 题目大意:给你$n$个模式串,求一个最短且字典序最小的文本串并输出这个串,$n<=12,len<=50$ 首先对所有模式串构造$Trie$图,$Trie ...
随机推荐
- ABP Linq 扩展的 WhereIf 查询内部实现
public static class QueryableExtensions { public static IQueryable<T> WhereIf<T>(this IQ ...
- ORM下实现继承的三种方式(TPH TPC TPT)
TPH(Table Per Hierarchy):所有的数据都放在同一个表格内,但是使用辨别标志(Discriminator)的方式来区分 TPC(Table Per Concrete-Type):由 ...
- Nodejs之路(四)—— MongoDB&MySQL
一.MongoDB 1.1概述 MongoDB 是一个基于分布式文件存储的数据库.由 C++ 语言编写.旨在为 WEB 应用提供可扩展的高性能数据存储解决方案.MongoDB 是一个介于关系数据库和非 ...
- SpringCloud学习笔记《---01 概念 ---》篇
- 创建 linuxrc 文件
创建 linuxrc,加入如下内容: [arm@localhost my_rootfs]#vi linuxrc #!/bin/sh #挂载/etc 为 ramfs, 并从/mnt/etc 下拷贝文件到 ...
- [Luogu2135] 方块消除【区间Dp】
Online Judge:P2135 方块消除(这题不用预处理) Label:区间Dp 题目描述 Jimmy最近迷上了一款叫做方块消除的游戏.游戏规则如下:n个带颜色方格排成一列,相同颜色的方块连成一 ...
- [转]gnome环境中将家目录下预设的文件夹由中文名称改为英文名称
参考文章:gnome环境中将家目录下预设的文件夹由中文名称改为英文名称 打开终端 1 修改语言环境为英文 export LANG=en_US 如果想修改语言环境为日语,可执行 export LANG= ...
- SpringBoot_02_SpringBoot的配置文件
1.SpringBoot配置文件 SpringBoot是基于约定的,所以很多配置都有默认值,但如果想使用自己的配置替换默认配置的话,就可以使用application.properties或者appli ...
- JavaScript 数组(Array)方法(二)
forEach ES5新增的方法,Arr.forEach((value, index,array)=>{}); let arr=['a','b','c']; arr.forEach((val,i ...
- SpringBoot学习笔记(六):SpringBoot实现Shiro登录控制和权限控制
登录模块:在登录时必须调用 授权模块:不是一登录就调动,而是当角色权限控制时才会调用 登录控制 环境搭建在上一篇. 数据库表 表名:role 字段:id rolename 表名:user 字段:id ...