P1979 华容道

题目描述

【问题描述】

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

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

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

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

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

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

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

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

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

输入输出格式

输入格式:

输入文件为 puzzle.in。

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

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

输出格式:

输出文件名为 puzzle.out。

输出有 q 行,每行包含 1 个整数,表示每次游戏所需要的最少时间,如果某次游戏无法完成目标则输出−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)上与当前图中目标位置上的棋子交换位置,之后能与空白块交换位置的只有当前图中目标位置上的那个棋子,因此目标棋子永远无法走到它的目标位置, 游戏无

法完成。

【数据范围】

对于 30%的数据,1 ≤ n, m ≤ 10,q = 1;

对于 60%的数据,1 ≤ n, m ≤ 30,q ≤ 10;

对于 100%的数据,1 ≤ n, m ≤ 30,q ≤ 500。

思路:

考虑一下怎么做这题:

  ①灵活改变状态表示方法,整合无用状态,减少无用状态

  ②将状态抽离,构图,转化为图论问题

故:正确的步骤为

第一步,抽离有用状态

第二步,有用状态与有用的后继状态 连边构图,

    边权为:由有用状态 到 后继状态 所需的最小步数

第三步,初始状态 到 目标状态 ,跑最短路(最好是跑spfa)

            所以正解就出来啦~~即bfs+spfa

技巧:

我们可以用0,1,2,3分别表示空白格在指定格的上右下左

状态=((行号-1)*列数+(列号-1))*4+ 0/1/2/3

int numbers(int i,int j) { return (i-1)*m+j-1<<2; } 状态=number(x,y)+0/1/2/3

其实所有网格图中的状态都可以采取类似的方法

状态编号= 图中编号* S+ x ,x∈[0,S) S=每种编号状态数

所以在定义dx数组跟dy数组的时候一定要一一对应!!!

坑点:

①如上技巧所述:

  在定义dx数组跟dy数组的时候一定要一一对应!!!不然很吃亏...

②写n,m千万不要写反了...写反了很尴尬,读不进去...虽然是可以检查出来,不过这个真的很致命!!!!

③在进行连边的时候,其实还有第四种后继状态:

  就是空白格与指定格的位置互换

  我使用的是上右下左这样的dx,dy,所以第四种状态的转换就简单的变为{空白格的numbers+(d+2)%4}    ps:d是枚举的初始状态空白格相对于指定格的方向。

  除此之外的状态用指定格的numbers+d即可

代码:

因为我细心的敲了好多注释,快夸我快夸我=w=

#include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
#define INF 0x7fffffff
using namespace std;const int Maxt = 32;
const int Maxn = 3610;
const int Maxm = Maxn * 5;
int n,m,p;
bool a[Maxt][Maxt],vis[Maxn];
int dx[4] = {-1,0,1, 0},dy[4] = { 0,1,0,-1};
int predis[Maxt][Maxt],dis[Maxn];struct game {int next,to,w;
}e[Maxm];
int top,head[Maxn];
void add(int u,int v,int val) {top++;e[top].to=v;e[top].w=val;e[top].next=head[u];head[u]=top;
}struct start {int x,y;
} nxt,cur;
queue<start>q;
queue<int>que;int numbers(int i,int j) {/*因为固定一个指定块之后的状态对应着5种状态,都是不同的状态,所以要开5倍,即必须编号多编 */j--; ///从0开始编号 return (i-1)*m+j<<2;
//  return (i-1)*m+j<<2;///如果像这样从1开始编号的话,我们的存图的struct game e数组就需要多开5个空间!!!所以还是最好从0编号好
}void bfs(int ex,int ey,int px,int py,int d) {int cx,cy,nx,ny; memset(predis,-1,sizeof(predis));/*predis数组表示啥呢??? 其实简单来说就是:固定指定块,空白块在指定块的其中一个方向然后再由这个状态到其他状态的最短路没错!储存的就是最短路的距离!!! 其他状态是指:固定了指定块之后,空白块在指定块的另外三个方向*////指定格  predis[px][py]=1;///空白格predis[ex][ey]=0;///bfs当前空白格移动到每个点所需要的步数 cur.x=ex,cur.y=ey;q.push(cur);while(!q.empty()) {cur=q.front();q.pop();cx=cur.x,cy=cur.y;for(int i=0; i<4; ++i) {nx=cur.x+dx[i],ny=cur.y+dy[i];if(a[nx][ny] && predis[nx][ny]==-1) {predis[nx][ny]=predis[cx][cy]+1;nxt.x=nx,nxt.y=ny;q.push(nxt);}}}///如刚才所述 if(d==8) return;int tmp=numbers(px,py);for(int i=0; i<4; ++i) {int x=px+dx[i],y=py+dy[i];if(predis[x][y]>0)/*因为在之前已经把predis[ex][ey]赋值为0,所以这个条件就是相当于当前枚举的这个指定块与空白块的状态,与它的后继状态进行连边 *//*----------这里重要----------*/ add(tmp+d,tmp+i,predis[x][y]);/*----------真的重要----------*/}/*最后一种后继状态是指定格与空白格交换位置因为规定的0123是上右下左,所以(d+2)%4可以直接转化为对面的方向交换相当于走一步,所以边权为1 */add(tmp+d,numbers(ex,ey)+(d+2)%4,1);
}void spfa(int sx,int sy) {int tmp;memset(dis,-1,sizeof(dis));for(int i=0; i<4; ++i) {int x=sx+dx[i],y=sy+dy[i];if(predis[x][y]!=-1) {tmp=numbers(sx,sy)+i;dis[tmp]=predis[x][y];que.push(tmp);}}int u;while(!que.empty()) {u=que.front();que.pop();vis[u]=false;for(int i=head[u]; i; i=e[i].next) {int v=e[i].to;if(dis[v]==-1 || dis[v]>dis[u]+e[i].w) {dis[v]=dis[u]+e[i].w;if(!vis[v]) {vis[v]=true;que.push(v);}}}}
} int main() {scanf("%d%d%d",&n,&m,&p);///枚举并固定指定格 for(int i=1; i<=n; ++i)for(int j=1; j<=m; ++j)scanf("%d",&a[i][j]);for(int i=1; i<=n; ++i)for(int j=1; j<=m; ++j)if(a[i][j]) {///枚举固定空白格,上,右,下,左(顺时针旋转,方便进行转换) ///bfs在固定指定格与空白格的5种状态 if(a[i-1][j]) bfs(i-1,j,i,j,0); if(a[i][j+1]) bfs(i,j+1,i,j,1);if(a[i+1][j]) bfs(i+1,j,i,j,2);if(a[i][j-1]) bfs(i,j-1,i,j,3);}/*空白格:ex,ey 指定格的初始位置:sx,sy 目标位置:tx,ty */int ex,ey,sx,sy,tx,ty,ans;while(p--) {scanf("%d%d%d%d%d%d",&ex,&ey,&sx,&sy,&tx,&ty);if(sx==tx && sy==ty) {printf("0\n");continue;}/*-----------最后一次bfs-----------*//*因为我们是直接枚举空白快与指定块连接在一起的状态,但是他们其实有可能初始状态并没有连接,所以强行将她们进行连接,但是不在图中进行连边,所以随便弄个d,如果bfs之后要建边了,若是胡乱搞的这个d,直接return即可 */ bfs(ex,ey,sx,sy,8);///普♂通♀的spfa,跑最短路
        spfa(sx,sy);ans=INF;///将目标位置进行编号 int tmp=numbers(tx,ty);for(int i=0; i<4; ++i)///+i是空白块的状态方向if(dis[tmp+i]!=-1) ///往小里面更新 ans=min(ans,dis[tmp+i]);if(ans==INF)ans=-1;printf("%d\n",ans); }return 0;
}

转载于:https://www.cnblogs.com/zxqxwnngztxx/p/7348987.html

luogu P1979 华容道相关推荐

  1. [洛谷luogu] P1979 [NOIP2013T6]华容道

    60分做法:bfs四维大暴搜 O(q(nm^2)).空白块 初始位置的块:指定块 目标位置的块:目标块f[a][b][c][d] a b 表指定块 c d表空白块 题解做法: 我们要把指定块通过空白块 ...

  2. [NOIP2013] 提高组 洛谷P1979 华容道

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

  3. 洛谷P1979 华容道(dfs)

    由于不太会打bfs,这题用的dfs. 通过这道题还是学到了蛮多的东西的. 首先,瞎打一气(dfs + 标记走过的点)40分. 代码如下: #include<iostream> #inclu ...

  4. 【题解】P1979 [NOIP2013 提高组] 华容道(SPFA,BFS,常数优化)

    [题解]P1979 [NOIP2013 提高组] 华容道 最近打比赛次次挂..平均每周得被至少一场比赛打击一次(这周好不容易 ABC 打的还行模拟赛又挂--)心烦意乱.写篇题解疏散一下内心的苦闷(雾) ...

  5. P1979 [NOIP]华容道

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

  6. P1979 [NOIP2013 提高组] 华容道

    题目来源 [NOIP2013 提高组] 华容道 - 洛谷 题目考点 搜索   图论 题目 小 B 最近迷上了华容道,可是他总是要花很长的时间才能完成一次.于是,他想到用编程来完成华容道:给定一种局面, ...

  7. luogu P1549 棋盘问题(2) 题解

    luogu P1549 棋盘问题(2) 题解 题目描述 在\(N * N\)的棋盘上\((1≤N≤10)\),填入\(1,2,-,N^2\)共\(N^2\)个数,使得任意两个相邻的数之和为素数. 例如 ...

  8. [Luogu] 选学霸

    https://www.luogu.org/problemnew/show/P2170 并查集+DP #include <iostream> #include <cstring> ...

  9. Luogu 2470 [SCOI2007]压缩

    和Luogu 4302 [SCOI2003]字符串折叠 差不多的想法,区间dp 为了计算方便,我们可以假设区间[l, r]的前面放了一个M,设$f_{i, j, 0/1}$表示区间$[i, j]$中是 ...

最新文章

  1. NYOJ 252 01串 dp
  2. sort函数——利用函数实现快速排序c++
  3. Freemarker 页面静态化技术使用入门案例
  4. lombok不生效问题(持续补充)
  5. 深入了解Java之虚拟机内存
  6. 如何学习 azure_Azure的监督学习
  7. vue 截取视频第一帧
  8. Fedora 13 正确安装 VirtualBox 3.2.x 的 步骤
  9. 数据库实验6 数据库的分组查询和统计查询
  10. css单行文本和多行文本溢出实现省略号显示
  11. 专升本/四六级/考研英语学习资源汇总
  12. MySQL入门系列:视图
  13. 数字疗法001 | 心理疾病太痛苦。把你的心理健康交给昭阳医生吧
  14. 关于两个模块同时使用Arduino Mega硬串口问题
  15. 37 篇! Facebook 今年被 CVPR 收录的论文都说了啥?
  16. 计算机网络模拟校园,计算机网络课程计-模拟校园网组网实验.doc
  17. [2]#hdu1219AC Me
  18. 华为三层交换机 配置
  19. 深度学习目标检测最全综述
  20. 一、python简介(吉多•范罗苏姆:人生苦短,我用python)

热门文章

  1. 计算机在会计专业的作用论文开题报告,会计电算化对传统会计的影响开题报告.docx...
  2. 【BZOJ3875】[Ahoi2014Jsoi2014]骑士游戏 SPFA优化DP
  3. 用java编写数组最小公倍数_java求多个数字的最小公倍数
  4. signature=755e8ee7e865b95708ea6b30aedefbd3,fMRI of Language Systems
  5. aria2网页服务器错误,配置aria2服务器错误
  6. arcgis之合并碎小图斑到相邻大块图斑
  7. python 删除excel空行_Java 删除 Excel 中的空白行和列
  8. 计算机中模板与母版的区别,模板和模版有啥区别?
  9. 构建安全可靠的微服务 | Nacos 在颜铺 SaaS 平台的应用实践
  10. java JPanel设置边框和标题