codevs 3290 华容道(SPFA+bfs)
codevs 3290华容道
3290 华容道
2013年NOIP全国联赛提高组
时间限制: 1 s 空间限制: 128000 KB
题目描述 Description
小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次。于是,他想到用编程来完成华容道:给定一种局面,华容道是否根本就无法完成,如果能完成,最少需要多少时间。
小 B 玩的华容道与经典的华容道游戏略有不同,游戏规则是这样的:
在一个 n*m 棋盘上有 n*m 个格子,其中有且只有一个格子是空白的,其余 n*m-1个格子上每个格子上有一个棋子,每个棋子的大小都是 1*1 的;
有些棋子是固定的,有些棋子则是可以移动的;
任何与空白的格子相邻(有公共的边)的格子上的棋子都可以移动到空白格子上。 游戏的目的是把某个指定位置可以活动的棋子移动到目标位置。
给定一个棋盘,游戏可以玩 q 次,当然,每次棋盘上固定的格子是不会变的,但是棋盘上空白的格子的初始位置、指定的可移动的棋子的初始位置和目标位置却可能不同。第 i 次玩的时候,空白的格子在第 EX_i 行第 EY_i 列,指定的可移动棋子的初始位置为第 SX_i 行第 SY_i 列,目标位置为第 TX_i 行第 TY_i 列。
假设小 B 每秒钟能进行一次移动棋子的操作,而其他操作的时间都可以忽略不计。请你告诉小 B 每一次游戏所需要的最少时间,或者告诉他不可能完成游戏。
输入描述 Input Description
第一行有 3 个整数,每两个整数之间用一个空格隔开,依次表示 n、m 和 q;
接下来的 n 行描述一个 n*m 的棋盘,每行有 m 个整数,每两个整数之间用一个空格隔开,每个整数描述棋盘上一个格子的状态,0 表示该格子上的棋子是固定的,1 表示该格子上的棋子可以移动或者该格子是空白的。
接下来的 q 行,每行包含 6 个整数依次是 EX_i、EY_i、SX_i、SY_i、TX_i、TY_i,每两个整数之间用一个空格隔开,表示每次游戏空白格子的位置,指定棋子的初始位置和目标位置。
输出描述 Output Description
输出有 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
数据范围及提示 Data Size & Hint
【样例说明】
棋盘上划叉的格子是固定的,红色格子是目标位置,圆圈表示棋子,其中绿色圆圈表示目标棋子。
第一次游戏,空白格子的初始位置是 (3, 2)(图中空白所示),游戏的目标是将初始位置在(1, 2)上的棋子(图中绿色圆圈所代表的棋子)移动到目标位置(2, 2)(图中红色的格子)上。
移动过程如下:
第二次游戏,空白格子的初始位置是(1, 2)(图中空白所示),游戏的目标是将初始位置在(2, 2)上的棋子(图中绿色圆圈所示)移动到目标位置 (3, 2)上。
要将指定块移入目标位置,必须先将空白块移入目标位置,空白块要移动到目标位置,必然是从位置(2,2)上与当前图中目标位置上的棋子交换位置,之后能与空白块交换位置的只有当前图中目标位置上的那个棋子,因此目标棋子永远无法走到它的目标位置,游戏无法完成。
【数据范围】
对于 30%的数据,1 ≤ n, m ≤ 10,q = 1;
对于 60%的数据,1 ≤ n, m ≤ 30,q ≤ 10;
对于 100%的数据,1 ≤ n, m ≤ 30,q ≤ 500。
题意:
求将指定的棋子从起始位置移到目标位置最少的步数。
想法:
假如我是在考场上遇到这题,我大抵只会写bfs+剪枝了。
现在分析下题目:
假如你已经选好从起点到终点的路径,那怎么求最小步数?
应该是先将空白格移到指定棋子的四周(即指定棋子要走的下一步),然后让指定棋子走下去。然后再将空白格移通过其他最小路径到指定棋子的下一步,当然不能经过指定棋子,不然就是往回走了。一直走下去,最后就得到了对于已知路径的最小步数。
(ps:绿色为空白格,蓝色为指定棋子的位置,红色为目标位置,橘色线段为要走的路径,紫色箭头为空白格移动方向,浅绿色为空白格移动轨迹,黄色为空白格在目标位置上。)
现在再看看题目的数据:
n,m<=30,q<=500.每次询问都是同一张图,只有起始、目标、空白位置有改变。那么在已知空白格位置时,对于每个棋子要往四周走的话,其最小步数是可以预处理。那么假设空白格子在一个棋子的d1方向上相邻格子上,那么这个棋子要往d2方向走的话,就可以不用知道空白格的位置直接预处理了。
开一个数组cost[x][y][d1][d2]表示棋子(x,y)在相邻d1方向上有个空白格,要往相邻d2方向走一格的最小步数。
这样的话,我们可以先将空白格移到起始位置四周,然后对于棋子走的每一步都可以跳过计算空白格移动到棋子要走的下一步的最小步数了。那么对于之后每一步棋子空白格都在其四周。所以就可以这样定义一个数组:
dist[x][y][d1]表示棋子(x,y)空白格在其d1方向相邻格子上时,从起点状态到这里最小步数。从而代替原本的纯bfs中的dist[x][y][x1][y1]表示棋子(x,y),空白格(x1,y1)起点状态到这里的最小步数。
这时候就可以用最短路求起点状态到终点状态的最小步数。终点有四个状态:dist[tx,ty,d1]1<=d1<=4四个方向。ans=min{dist[tx,ty,d1]}1<=d1<=4
这里再考虑起点状态,先计算将空白格移到起点的一个方向相邻格子上的最小步数,因为权值均为1,所以bfs就好。然后spfa算dist数组。对于每个不同的起点状态,答案取最小的。
那么什么是无解?算一下最大的最小步数,大概是只有一条很长的路径即(n*m div 2 蛇形环形路径)。每次棋子走一步,空白格要绕一圈回来,再走下一步。大概是n*m*n*m=810000的样子,那么我们将dist数组初值设为maxlongint,就可以区别是否有解了。
还有一种特殊情况:一开始,指定棋子就在目标位置上了.....(聪明的小B看不出?)
所以时间复杂度O(n*m*n*m+q*n*m)=枚举(n*m)*bfs(n*m)+q*spfa(k*m*n),k作为常数比较小。
#include<cstdio>
#include<cstring>
const int N=,d[][]={{,},{,},{-,},{,-}},inf=;
struct xint{int x,y;}q[N*N];
struct yint{int x,y,dir;}qq[N*N*];
int n,m,T,h,t,ex,ey,sx,sy,tx,ty;
int a[N][N],dis[N][N],di[N][N][][],dist[N][N][];
bool b[N][N][];
bool ok(int x,int y){return x>&&x<=n&&y>&&y<=m&&a[x][y];}
void yuchuli(int sx,int sy)//预处理每个点四个方向上空格到另外方向的移动次数(不经过这个点)
{
for (int tql=;tql<;tql++)//tql=太强啦
{
int nzql=sx+d[tql][],wzrl=sy+d[tql][];//nzql=您最强啦 wzrl=我最弱了
if (ok(nzql,wzrl))
{
h=t=;
q[]=(xint){nzql,wzrl};
memset(dis,inf,sizeof dis);
dis[nzql][wzrl]=;
while (h<=t)
{
int x=q[h].x,y=q[h].y;
h++;
for (int i=;i<;i++)
{
int xx=x+d[i][],yy=y+d[i][];
if (ok(xx,yy)&&(xx!=sx||yy!=sy)&&dis[xx][yy]==inf)
q[++t]=(xint){xx,yy},dis[xx][yy]=dis[x][y]+;
}
}
for (int i=;i<;i++)
if (i!=tql)
{
int x=sx+d[i][],y=sy+d[i][];
if (ok(x,y)&&dis[x][y]!=inf)
di[sx][sy][tql][i]=dis[x][y];
}
/*if (sx==2&&sy==2&&tql==1)
for (int i=1;i<=n;i++)
{
for (int j=1;j<=m;j++)
printf("%d ",dis[i][j]);
printf("\n");
}*/
}
}
//if (sx==2&&sy==2)
// printf("%d\n",di[sx][sy][1][0]);
}
void yuchuli2()//预处理一开始空格到棋子初始位置四周的移动次数(不经过棋子)
{
h=t=;
q[]=(xint){ex,ey};
memset(dis,inf,sizeof dis);
dis[ex][ey]=;
while (h<=t)
{
int x=q[h].x,y=q[h].y;
h++;
for (int i=;i<;i++)
{
int xx=x+d[i][],yy=y+d[i][];
if (ok(xx,yy)&&(xx!=sx||yy!=sy)&&dis[xx][yy]==inf)
q[++t]=(xint){xx,yy},dis[xx][yy]=dis[x][y]+;
}
}
}
void solve()//spfa
{
scanf("%d%d%d%d%d%d",&ex,&ey,&sx,&sy,&tx,&ty);
if (sx==tx&&sy==ty) {printf("0\n"); return;}
yuchuli2();
memset(dist,inf,sizeof dist);
memset(b,,sizeof b);
h=; t=;
for (int i=;i<;i++)
{
int x=sx+d[i][],y=sy+d[i][];
if (ok(x,y)&&dis[x][y]!=inf)
qq[++t]=(yint){sx,sy,i},dist[sx][sy][i]=dis[x][y],b[sx][sy][i]=;
}
while (h<=t)
{
int x=qq[h].x,y=qq[h].y,dir=qq[h].dir;
h++;
b[x][y][dir]=;
//棋子与空格交换
int xx=x+d[dir][],yy=y+d[dir][],dire=(dir+)%;
if (dist[xx][yy][dire]>dist[x][y][dir]+)
{
dist[xx][yy][dire]=dist[x][y][dir]+;
if (!b[xx][yy][dire])
{
qq[++t]=(yint){xx,yy,dire},b[xx][yy][dire]=; //if (xx==2&&yy==2&&dire==0) printf("%d %d %d\n",x,y,dir);
}
}
//空格移动到另一方向上
for (int i=;i<;i++)
if (i!=dir&&ok(x+d[i][],y+d[i][])&&di[x][y][dir][i]!=inf&&dist[x][y][i]>dist[x][y][dir]+di[x][y][dir][i])
{
dist[x][y][i]=dist[x][y][dir]+di[x][y][dir][i];
if (!b[x][y][i])
qq[++t]=(yint){x,y,i},b[x][y][i]=;
//if (x==2&&y==2&&i==0) printf("%d %d %d\n",x,y,dir);
}
}
}
//for (int i=1;i<=t;i++)
// printf("%d %d %d\n",qq[i].x,qq[i].y,qq[i].dir);
int ans=inf;
for (int i=;i<;i++)
if (dist[tx][ty][i]<ans)
ans=dist[tx][ty][i];
if (ans==inf) printf("-1\n");
else printf("%d\n",ans);
}
int main()
{
memset(di,inf,sizeof di);
scanf("%d%d%d",&n,&m,&T);
for (int i=;i<=n;i++)
for (int j=;j<=m;j++)
scanf("%d",&a[i][j]);
for (int i=;i<=n;i++)
for (int j=;j<=m;j++)
if (a[i][j])
yuchuli(i,j);
while (T--) solve();
return ;
}
codevs 3290 华容道(SPFA+bfs)的更多相关文章
- codevs 3290 华容道
HAHAHA BFS+SPFA. #include<iostream> #include<cstdio> #include<cstring> #include< ...
- ACM/ICPC 之 最短路-Floyd+SPFA(BFS)+DP(ZOJ1232)
这是一道非常好的题目,融合了很多知识点. ZOJ1232-Adventrue of Super Mario 这一题折磨我挺长时间的,不过最后做出来非常开心啊,哇咔咔咔 题意就不累述了,注释有写,难点在 ...
- Educational Codeforces Round 54 (Rated for Div. 2) D Edge Deletion (SPFA + bfs)
题目大意:给定你一个包含n个点m条边的无向图,现在最多在图中保留k条边,问怎么删除多的边,使得图中良好的节点数最多,求出保留在图中的边的数量和编号. 良好的节点定义为:删除某条边后该点到点1的最短距离 ...
- 【CodeVS 3290】【NOIP 2013】华容道
http://codevs.cn/problem/3290/ 据说2013年的noip非常难,但Purpleslz学长还是AK了.能A掉这道题真心orz. 设状态$(i,j,k)$表示目标棋子在$(i ...
- bzoj P1979 华容道【bfs+spfa】
调死我了-- 首先观察移动方式,需要移动的格子每次移动到相邻格子,一定是先把空白格子挪过去,所以我们得到一种做法,就是bfs预处理出每一个格子的四联通格子之间的空白格子移动距离建边,注意这个移动是不能 ...
- P1979 华容道 spfa题解
题目描述 [问题描述] 小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面, 华容道是否根本就无法完成,如果能完成, 最少需要多少时间. 小 ...
- Prime Path[POJ3126] [SPFA/BFS]
描述 孤单的zydsg又一次孤单的度过了520,不过下一次不会再这样了.zydsg要做些改变,他想去和素数小姐姐约会. 所有的路口都被标号为了一个4位素数,zydsg现在的位置和素数小姐姐的家也是这样 ...
- [luoguP3110] [USACO14DEC]驮运Piggy Back(SPFA || BFS)
传送门 以 1,2,n 为起点跑3次 bfs 或者 spfa 那么 ans = min(ans, dis[1][i] * B + dis[2][i] * E + dis[3][i] * P) (1 & ...
- NOIP2013华容道(BFS+乱搞)
n<=30 * m<=30 的地图上,0表示墙壁,1表示可以放箱子的空地.q<=500次询问,每次问:当空地上唯一没有放箱子的空格子在(ex,ey)时,把位于(sx,sy)的箱子移动 ...
随机推荐
- 前端性能利器——dynatrace ajax edition
因为最近的工作跟性能分析有关系,所以写个小总结. 顺带推荐两个我常用的小工具: 1.文件对比工具beyond compare,非常好用,对比.修改很简单.当然我只是用的试用版本.google一下官网下 ...
- 1、Java背景、标示符、运算符、转义字符
一.Java平台: 1.Java的创建:1991年由SUN公司创建. 2.Java的特点:面向对象.可移植性.多线程.分布式.可靠.安全. 3.Java的三个架构:JavaEE.JavaSElect. ...
- 利用百度语音API进行语音识别。
由于项目需要,这几天都在试图利用百度语音API进行语音识别.但是识别到的都是“啊,哦”什么的,我就哭了. 这里我只是分享一下这个过程,错误感觉出现在Post语音数据那一块,可能是转换问题吧. API请 ...
- svg技术(可缩放矢量图形)介绍
公司里面的产品用图表的地方也比较多,作为平台维护的我,收到几次需求提的建议中包括图表美化的功能,要炫,要3d,立体感,功能要强大等到:平台现有控件都是用的一个开源flash,我对flash虽然会一点但 ...
- [转]C++模板学习
1. 模板的概念. 我们已经学过重载(Overloading),对重载函数而言,C++的检查机制能通过函数参数的不同及所属类的不同.正确的调用重载函数.例如,为求两个数的最大值,我们定义MAX()函数 ...
- 用MVC的辅助方法自定义了两个控件:“可编辑的下拉框控件”和“文本框日历控件”
接触MVC也没多长时间,一开始学的时候绝得MVC结构比较清晰.后来入了门具体操作下来感觉MVC控件怎么这么少还不可以像ASP.net form那样拖拽.这样设计界面来,想我种以前没学过JS,Jquer ...
- java中堆栈(stack)和堆(heap)(还在问静态变量放哪里,局部变量放哪里,静态区在哪里.....进来)
(1)内存分配的策略 按照编译原理的观点,程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的. 静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编 译时就可以给 ...
- Javascript高级程序设计——函数
函数Function 通过函数封装多条语句,在任何地方执行.javascript函数不会重载,相同名字函数,名字属于后定义的函数通过function关键词声明. function functionNa ...
- 绝不要进行两层间接非const指针赋值给const指针
#include <stdio.h> #include <stdlib.h> int main(void) { int *p1; int * *pp1; const int * ...
- 02快速学习ExtJs之---第一个HelloWord!
这篇主要讲部署下ExtJS开发环境,以及搭建项目.我们使用ExtJs官方提供的Sencha Cmd来搭建 1.搭建项目 1.下载官方的Sencha Cmd工具,安装. 2..Window用户进入到命令 ...