P1979 华容道

题目描述

小\(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\) 。

说明

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

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

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


对于我这种弱水平的选手,一定要好好练部分分。

注意到我们可以在每次游戏枚举空白点位置和操作点位置进行搜索,则复杂度为\(O(n^2m^2q)\),在考场上想不到正解时,一定要把这部分分拿到。

在洛谷神机下拿了80分,事实上应该只有60分

60~80pts

  1. #include <cstdio>
  2. #include <queue>
  3. using namespace std;
  4. const int N=32;
  5. int g[N][N],n,m,vis[N][N][N][N],q;
  6. int ex,ey,sx,sy,tx,ty,t,ans;
  7. const int dx[5]={0,-1,0,1,0};
  8. const int dy[5]={0,0,1,0,-1};
  9. int abs(int x){return x>0?x:-x;}
  10. struct node
  11. {
  12. int x0,y0,x1,y1,step;
  13. node(int x0,int y0,int x1,int y1,int step)
  14. {
  15. this->x0=x0;this->y0=y0;this->x1=x1;this->y1=y1;this->step=step;
  16. }
  17. };
  18. void bfs()
  19. {
  20. queue <node > q;
  21. node st(ex,ey,sx,sy,0);
  22. q.push(st);
  23. while(!q.empty())
  24. {
  25. int x0=q.front().x0,y0=q.front().y0,x1=q.front().x1,y1=q.front().y1,step=q.front().step;
  26. if(x1==tx&&y1==ty) {ans=step;return;}
  27. q.pop();
  28. if(vis[x0][y0][x1][y1]==t) continue;
  29. vis[x0][y0][x1][y1]=t;
  30. if(x0==x1&&abs(y0-y1)==1)
  31. {
  32. node tt(x0,y1,x1,y0,step+1);
  33. q.push(tt);
  34. }
  35. if(y0==y1&&abs(x0-x1)==1)
  36. {
  37. node tt(x1,y0,x0,y1,step+1);
  38. q.push(tt);
  39. }
  40. for(int i=1;i<=4;i++)
  41. {
  42. int X=x0+dx[i],Y=y0+dy[i];
  43. if(g[X][Y]&&(X!=x1||Y!=y1))
  44. {
  45. node tt(X,Y,x1,y1,step+1);
  46. q.push(tt);
  47. }
  48. }
  49. }
  50. }
  51. int main()
  52. {
  53. scanf("%d%d%d",&n,&m,&q);
  54. for(int i=1;i<=n;i++)
  55. for(int j=1;j<=m;j++)
  56. scanf("%d",&g[i][j]);
  57. for(t=1;t<=q;t++)
  58. {
  59. scanf("%d%d%d%d%d%d",&ex,&ey,&sx,&sy,&tx,&ty);
  60. ans=-1;bfs();
  61. printf("%d\n",ans);
  62. }
  63. return 0;
  64. }

代码也不长

后来胡乱搞了搞A*结果比暴力还慢,表示不会。。


考虑正解,我们发现如果我们要移动的那个点要移动,则必须带着空白一起移动,否则没有意义。

每次询问,我们可以先把空白移到初始点旁边再说,对之后的状态,一定是点带空白走

对于原图的每个位置的点,它可以带四个方向的空白,于是把它拆成四个点建图。

边为拆的四个点互相进行连边权值要求,和空白与点交换的边权1,可以在询问前预处理。

然后对于每个询问,我们先移动空白点到初始点,然后枚举四个方向跑最短路即可

注意不需要移动的情况,否则可能比暴力分还低,洛谷的数据是65分

复杂度:\(O(n^2m^m+q*(knm))\)


Code:

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <queue>
  4. using namespace std;
  5. const int N=32;
  6. const int M=3610;
  7. const int inf=0x3f3f3f3f;
  8. int g[N][N],n,m,q,ex,ey,sx,sy,tx,ty;
  9. int cal(int x,int y,int towards)
  10. {
  11. return ((x-1)*m+y)+(towards-1)*n*m;
  12. }
  13. const int dx[5]={0,-1,0,1,0};
  14. const int dy[5]={0,0,1,0,-1};
  15. int head[M],to[M<<3],Next[M<<3],edge[M<<3],cnt;
  16. void add(int u,int v,int w)
  17. {
  18. to[++cnt]=v;Next[cnt]=head[u];edge[cnt]=w;head[u]=cnt;
  19. }
  20. struct node
  21. {
  22. int x,y,step;
  23. node(int x,int y,int step)
  24. {
  25. this->x=x;this->y=y;this->step=step;
  26. }
  27. };
  28. int used[N][N];
  29. int Dis(int x0,int y0,int Tx,int Ty)
  30. {
  31. memset(used,0,sizeof(used));
  32. queue <node > q;
  33. node t0(x0,y0,0);
  34. q.push(t0);
  35. while(!q.empty())
  36. {
  37. int x=q.front().x,y=q.front().y,step=q.front().step;
  38. q.pop();
  39. if(used[x][y]) continue;
  40. used[x][y]=1;
  41. if(x==Tx&&y==Ty) return step;
  42. for(int i=1;i<=4;i++)
  43. {
  44. int X=x+dx[i],Y=y+dy[i];
  45. if(g[X][Y])
  46. {
  47. node tt(X,Y,step+1);
  48. q.push(tt);
  49. }
  50. }
  51. }
  52. return inf;
  53. }
  54. void init()
  55. {
  56. scanf("%d%d%d",&n,&m,&q);
  57. for(int i=1;i<=n;i++)
  58. for(int j=1;j<=m;j++)
  59. scanf("%d",&g[i][j]);
  60. for(int i=1;i<=n;i++)
  61. for(int j=1;j<=m;j++)
  62. {
  63. if(!g[i][j]) continue;
  64. g[i][j]=0;
  65. for(int k=1;k<=4;k++)
  66. {
  67. int X=dx[k]+i,Y=dy[k]+j;
  68. if(!g[X][Y]) continue;
  69. int u=cal(i,j,k),f=k>2?-1:1;
  70. int v0=cal(X,Y,k+f*2);
  71. add(u,v0,1),add(v0,u,1);
  72. for(int l=k+1;l<=4;l++)
  73. {
  74. int tX=dx[l]+i,tY=dy[l]+j;
  75. if(!g[tX][tY]) continue;
  76. int v=cal(i,j,l),w=Dis(X,Y,tX,tY);
  77. add(u,v,w),add(v,u,w);
  78. }
  79. }
  80. g[i][j]=1;
  81. }
  82. }
  83. int dis[M],vis[M];
  84. void spfa(int s)
  85. {
  86. memset(dis,0x3f,sizeof(dis));
  87. queue <int > q;
  88. q.push(s);
  89. dis[s]=0;
  90. while(!q.empty())
  91. {
  92. int u=q.front();
  93. vis[u]=0;
  94. q.pop();
  95. for(int i=head[u];i;i=Next[i])
  96. {
  97. int v=to[i];
  98. if(dis[v]>dis[u]+edge[i])
  99. {
  100. dis[v]=dis[u]+edge[i];
  101. if(!vis[v]) vis[v]=1,q.push(v);
  102. }
  103. }
  104. }
  105. }
  106. void work()
  107. {
  108. for(int i=1;i<=q;i++)
  109. {
  110. scanf("%d%d%d%d%d%d",&ex,&ey,&sx,&sy,&tx,&ty);
  111. if(tx==sx&&ty==sy) {printf("0\n");continue;}
  112. int ans=inf;
  113. for(int j=1;j<=4;j++)
  114. {
  115. int X=sx+dx[j],Y=sy+dy[j];
  116. if(!g[X][Y]) continue;
  117. g[sx][sy]=0;
  118. int cnt0=Dis(ex,ey,X,Y);
  119. g[sx][sy]=1;
  120. int u=cal(sx,sy,j);
  121. spfa(u);
  122. for(int k=1;k<=4;k++)
  123. {
  124. int tX=tx+dx[k],tY=ty+dy[k];
  125. if(!g[tX][tY]) continue;
  126. int v=cal(tx,ty,k);
  127. ans=min(ans,dis[v]+cnt0);
  128. }
  129. }
  130. if(ans==inf) printf("-1\n");
  131. else printf("%d\n",ans);
  132. }
  133. }
  134. int main()
  135. {
  136. init();
  137. work();
  138. return 0;
  139. }

2018.8.1

洛谷 P1979 华容道 解题报告的更多相关文章

  1. 洛谷 P2058 海港 解题报告

    P2058 海港 题目描述 小K是一个海港的海关工作人员,每天都有许多船只到达海港,船上通常有很多来自不同国家的乘客. 小K对这些到达海港的船只非常感兴趣,他按照时间记录下了到达海港的每一艘船只情况: ...

  2. 洛谷 P3956 棋盘 解题报告

    P3956 棋盘 题目描述 有一个\(m×m\)的棋盘,棋盘上每一个格子可能是红色.黄色或没有任何颜色的.你现在要从棋盘的最左上角走到棋盘的最右下角. 任何一个时刻,你所站在的位置必须是有颜色的(不能 ...

  3. BZOJ 3545 / 洛谷 P4197 Peaks 解题报告

    P4197 Peaks 题目描述 在\(\text{Bytemountains}\)有\(N\)座山峰,每座山峰有他的高度\(h_i\).有些山峰之间有双向道路相连,共\(M\)条路径,每条路径有一个 ...

  4. 洛谷P1979 华容道(70分 暴力)

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

  5. 虔诚的墓主人(BZOJ1227)(洛谷P2154)解题报告

    题目描述 小W是一片新造公墓的管理人.公墓可以看成一块N×M的矩形,矩形的每个格点,要么种着一棵常青树,要么是一块还没有归属的墓地. 当地的居民都是非常虔诚的基督徒,他们愿意提前为自己找一块合适墓地. ...

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

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

  7. 洛谷 P2672 推销员 解题报告

    P2672 推销员 题目描述 阿明是一名推销员,他奉命到螺丝街推销他们公司的产品.螺丝街是一条死胡同,出口与入口是同一个,街道的一侧是围墙,另一侧是住户.螺丝街一共有N家住户,第i家住户到入口的距离为 ...

  8. 洛谷 P2679 子串 解题报告

    P2679 子串 题目描述 有两个仅包含小写英文字母的字符串\(A\)和\(B\). 现在要从字符串\(A\)中取出\(k\)个互不重叠的非空子串,然后把这\(k\)个子串按照其在字符串\(A\)中出 ...

  9. 洛谷 P1076 寻宝 解题报告

    P1076 寻宝 题目描述 传说很遥远的藏宝楼顶层藏着诱人的宝藏.小明历尽千辛万苦终于找到传说中的这个藏宝楼,藏宝楼的门口竖着一个木板,上面写有几个大字:寻宝说明书.说明书的内容如下: 藏宝楼共有\( ...

随机推荐

  1. P/Invoke 光标的操作

    获取与设置光标在屏幕上的位置 GetCursorPos 获取光标在屏幕上的位置,光标位置始终是在屏幕坐标纵指定的,并且不受包含光标的窗口映射模式的影响 函数原型: BOOL GetCursorPos( ...

  2. Kotlin的密封(Sealed)类:超强的枚举(KAD 28)

    作者:Antonio Leiva 时间:Jun 27, 2017 原文链接:https://antonioleiva.com/sealed-classes-kotlin/ Kotlin的封装类是Jav ...

  3. Hbase restFul API

    获取hbase版本 curl -vi -X GET -H "Accept: text/xml" http://10.8.4.46:20550/version/cluster1.2. ...

  4. appium关键字:

    ## Appium 服务关键字 <expand_table> |关键字|描述|实例||----|-----------|-------||`automationName`|你想使用的自动化 ...

  5. 【Linux 运维】 安装PHP工具Composer

    一.安装PHP 由于Composer是 PHP 用来管理依赖(dependency)关系的工具.你可以在自己的项目中声明所依赖的外部工具库(libraries),Composer 会帮你安装这些依赖的 ...

  6. Trie 树——搜索关键词提示

    当你在搜索引擎中输入想要搜索的一部分内容时,搜索引擎就会自动弹出下拉框,里面是各种关键词提示,这个功能是怎么实现的呢?其实底层最基本的就是 Trie 树这种数据结构. 1. 什么是 "Tri ...

  7. maven项目中没有resource文件夹的问题

    之前使用eclipse创建maven项目,文件夹都是建好的,这几次创建,都没有resource文件夹,需要手动创建resource. 现象描述 在eclipse中,创建maven项目有两种方式: 一种 ...

  8. jdk1.8新特性-Lambda表达式使用要点

    前言 在jdk1.8出来的时候看到过,没怎么了解.但是最近再看kafka和spark框架,框架示例中ava版的很多地方用到Lambda表达式,发现使用Lambda表达式代码确实简单了好多,有些例子大致 ...

  9. Servlet过滤器介绍之原理分析

    zhangjunhd 的BLOG     写留言去学院学习发消息 加友情链接进家园 加好友 博客统计信息 51CTO博客之星 用户名:zhangjunhd 文章数:110 评论数:858 访问量:19 ...

  10. node包管理相关

    切换npm数据源 镜像使用方法(三种办法任意一种都能解决问题,建议使用第三种,将配置写死,下次用的时候配置还在): 1.通过config命令 npm config set registry https ...