由于不太会打bfs,这题用的dfs。

通过这道题还是学到了蛮多的东西的。

首先,瞎打一气(dfs + 标记走过的点)40分。
代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int X[] = {0,1,0,-1};
const int Y[] = {1,0,-1,0};
int m,n,ex,ey,a,b,c,d,ans,used[45][45][45][45],ma[50][50],q;
bool can(int x,int y)
{if(x < 1||x > n) return false;if(y < 1||y > m) return false;if(!ma[x][y]) return false;return true;
}
void dfs(int kx,int ky,int sx,int sy,int cnt)
{if(cnt >= ans) return;if(sx == ex&&sy == ey) {ans = cnt;return;}if(used[kx][ky][sx][sy] <= cnt) return;used[kx][ky][sx][sy] = cnt;for(int i = 0;i < 4;i ++){int x = kx + X[i],y = ky + Y[i];if(can(x,y)){if(x == sx&&y == sy) dfs(x,y,kx,ky,cnt+1);else dfs(x,y,sx,sy,cnt+1);}}
}
int main()
{scanf("%d %d %d",&n,&m,&q);for(int i = 1;i <= n;i ++)for(int j = 1;j <= m;j ++)scanf("%d",&ma[i][j]);for(int i = 1;i <= q;i ++){memset(used,0x3f,sizeof(used));ans = 1e6;scanf("%d %d %d %d %d %d",&a,&b,&c,&d,&ex,&ey);dfs(a,b,c,d,0);if(ans == 1e6) puts("-1");else printf("%d\n",ans);}
}

然后发现这个题没有办法剪枝,如果不标记路线会死循环,如果标记就没有办法记忆化搜索。

然后仔细观察了之前的程序,发现每一次起点走了一步,空位就要跑完整张图,而我们的目的是让起点移动到终点。所以我们只要让起点移动就可以了。

过程为,空格移动到起点的旁边,起点启动到空格,并且在这个过程中,空格不能经过起点。

所以我们要预处理一下,计算从a 点到b点不经过c点的最短距离,这个预处理的复杂度是O(n^6)的,这样会超时。

我们再考虑一下,起点和起点将要移动的位置一定是挨着的。因此我们只需要处理某一个点不经过上下左右4点然后反向找从起点将要移动的点不经过起点到空格的位置(4*n^4)。

85分代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int X[] = {0,1,0,-1};
const int Y[] = {1,0,-1,0};
int n,m,g,ma[35][35],dis[35][35][35][35][5],used[35][35],ans,a,b,c,d,ex,ey,vis[35][35][35][35];
struct zt
{int x,y;
};
queue<zt>q;
bool can(int x,int y)
{if(x < 1||x > n) return false;if(y < 1||y > m) return false;if(!ma[x][y]) return false;return true;
}
void spfa(int x,int y,int nx,int ny,int ji)
{while(!q.empty())   q.pop();memset(used,0,sizeof(used));q.push((zt){x,y});dis[x][y][x][y][ji] = 0;while(!q.empty()){zt u = q.front();q.pop();used[u.x][u.y] = 0;for(int i = 0;i < 4;i ++){int x1 = u.x + X[i],y1 = u.y + Y[i];if(can(x1,y1)&&(x1 != nx||y1 != ny)){if(dis[x][y][x1][y1][ji] > dis[x][y][u.x][u.y][ji] + 1){dis[x][y][x1][y1][ji] = dis[x][y][u.x][u.y][ji] + 1;if(!used[x1][y1]){q.push((zt){x1,y1});used[x1][y1] = 1;}}}}}
}
void dfs(int kx,int ky,int sx,int sy,int cnt)
{if(cnt >= ans) return;if(sx == ex&&sy == ey) {ans = cnt;return;}if(vis[sx][sy][kx][ky] <= cnt) return;vis[sx][sy][kx][ky] = cnt;for(int i = 0;i < 4;i ++){int x = sx + X[i],y = sy + Y[i];if(can(x,y))dfs(sx,sy,x,y,cnt+dis[x][y][kx][ky][(i+2)%4]+1);}
}
int main()
{scanf("%d%d%d",&n,&m,&g);for(int i = 1;i <= n;i ++)for(int j = 1;j <= m;j ++)scanf("%d",&ma[i][j]);memset(dis,0x3f,sizeof(dis));for(int i = 1;i <= n;i ++)for(int j = 1;j <= m;j ++){if(!ma[i][j]) continue;for(int k = 0;k < 4;k ++){a = i + X[k],b = j + Y[k];if(can(a,b)) spfa(i,j,a,b,k);}}for(int i = 1;i <= g;i ++){memset(vis,0x3f,sizeof(vis));ans = 1e6;scanf("%d%d%d%d%d%d",&a,&b,&c,&d,&ex,&ey);dfs(a,b,c,d,0);if(ans == 1e6) puts("-1");else printf("%d\n",ans);}
}

然后我们会发现将数组vis赋为极大值的复杂度是(500*35^4 = 750312500)。
这样对于后面的点,单是初始化就不够用了。

我们再观察一下,发现除未开始移动时,空位一定在起点四周。这样我们只要开一个大小为5的数组代替就可以。这样初始化为(500 * 35^2 * 5 = 3062500)。
这样做后会有90分。

这里再引入一个东西——启发式搜索。

启发式搜索会极大地减少重复经过的路径从而提高dfs的效率。简单说我们优先更新付出代价比较小的状态,这样这个状态就很难被再次更新,从而减少不必要的步骤。
AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int X[] = {0,1,0,-1};
const int Y[] = {1,0,-1,0};
int n,m,g,ma[35][35],dis[35][35][35][35][5],used[35][35],ans,a,b,c,d,ex,ey,vis[35][35][5];
struct zt
{int x,y;
};
queue<zt>q;
bool can(int x,int y)
{if(x < 1||x > n) return false;if(y < 1||y > m) return false;if(!ma[x][y]) return false;return true;
}
void spfa(int x,int y,int nx,int ny,int ji)
{while(!q.empty())   q.pop();memset(used,0,sizeof(used));q.push((zt){x,y});dis[x][y][x][y][ji] = 0;while(!q.empty()){zt u = q.front();q.pop();used[u.x][u.y] = 0;for(int i = 0;i < 4;i ++){int x1 = u.x + X[i],y1 = u.y + Y[i];if(can(x1,y1)&&(x1 != nx||y1 != ny)){if(dis[x][y][x1][y1][ji] > dis[x][y][u.x][u.y][ji] + 1){dis[x][y][x1][y1][ji] = dis[x][y][u.x][u.y][ji] + 1;if(!used[x1][y1]){q.push((zt){x1,y1});used[x1][y1] = 1;}}}}}
}
void dfs(int kx,int ky,int sx,int sy,int cnt,int k)
{if(cnt >= ans) return;if(sx == ex&&sy == ey) {ans = cnt;return;}if(vis[sx][sy][k] <= cnt) return;vis[sx][sy][k] = cnt;int used2[4] = {0,0,0,0},ji,lu;for(int i = 0;i < 4;i ++){lu = 1e6;for(int j = 0;j < 4;j ++){int x = sx + X[j],y = sy + Y[j];if(can(x,y)&&lu > dis[x][y][kx][ky][(j+2)%4]&&!used2[j])lu = dis[x][y][kx][ky][(j+2)%4],ji = j;}if(lu != 1e6){int x = sx + X[ji],y = sy + Y[ji];used2[ji] = 1;dfs(sx,sy,x,y,cnt+dis[x][y][kx][ky][(ji+2)%4]+1,(ji+2)%4);}   }
}
int main()
{scanf("%d%d%d",&n,&m,&g);for(int i = 1;i <= n;i ++)for(int j = 1;j <= m;j ++)scanf("%d",&ma[i][j]);memset(dis,0x3f,sizeof(dis));for(int i = 1;i <= n;i ++)for(int j = 1;j <= m;j ++){if(!ma[i][j]) continue;for(int k = 0;k < 4;k ++){a = i + X[k],b = j + Y[k];if(can(a,b)) spfa(i,j,a,b,k);}}for(int i = 1;i <= g;i ++){memset(vis,0x3f,sizeof(vis));ans = 1e6;scanf("%d%d%d%d%d%d",&a,&b,&c,&d,&ex,&ey);dfs(a,b,c,d,0,4);if(ans == 1e6) puts("-1");else printf("%d\n",ans);}
}

洛谷P1979 华容道(dfs)相关推荐

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

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

  2. 洛谷P1524 十字绣 dfs+思维

    前言 最近打算考研去软院,顺便向xmd了解了一点算法岗实习的行情,决定重拾算法能力,多多刷题. 现在在刷洛谷的题单和vijos的训练计划,听很多同学说leetcode很不错,打算把vj的训练计划刷完后 ...

  3. 洛谷 P1784 数独 dfs

    题目描述 数独是根据 9×9 盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行.每一列.每一个粗线宫内的数字均含 1 - 9 ,不重复.每一道合格的数独谜题都有且仅有唯一答案,推理方法也以此为 ...

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

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

  5. 洛谷 P1019 单词接龙 (DFS)

    题目传送门 当时一看到这题,蒟蒻的我还以为是DP,结果发现标签是搜索-- 这道题的难点在于思路和预处理,真正的搜索实现起来并不难.我们可以用一个贪心的思路,开一个dic数组记录每个单词的最小重复部分, ...

  6. *【洛谷 - P1025】数的划分(dfs 或 dp 或 母函数,第二类斯特林数Stirling)

    题干: 题目描述 将整数n分成k份,且每份不能为空,任意两个方案不相同(不考虑顺序). 例如:n=7,k=3,下面三种分法被认为是相同的. 1,1,5 1,5,1 5,1,1 问有多少种不同的分法. ...

  7. DFS(剪枝与优化) - 洛谷 P1361 - 小猫爬山

    DFS(剪枝与优化) - 洛谷 P1361 - 小猫爬山 翰翰和达达饲养了N只小猫,这天,小猫们要去爬山. 经历了千辛万苦,小猫们终于爬上了山顶,但是疲倦的它们再也不想徒步走下山了(呜咕>_&l ...

  8. 洛谷 P1238 走迷宫【搜索】【DFS】

    洛谷 P1238 走迷宫 一.题目链接 二.题目分析 (一)算法标签 (二)解题思路 三.AC代码 四.其它题解 一.题目链接 洛谷 P1238 走迷宫 二.题目分析 (一)算法标签 搜索 DFS ( ...

  9. dfs 洛谷 P1605 迷宫

    洛谷 P1605 迷宫 题目: 链接 https://www.luogu.com.cn/problem/P1605 思路:从出发点点出发dfs搜索每一次可以到达终点的路径,边界条件为不能走障碍物(在输 ...

最新文章

  1. vue-cli2.0创建项目步骤
  2. 游戏人物标记——腾讯笔试
  3. POJ 3172 (认真读题的题)
  4. mybatis框架执行原理简单实现
  5. php烟花效果,用p5.js制作烟花特效的示例代码
  6. 性能分析工具Linux perf使用经验
  7. 菜鸟的MySQL学习笔记(二)
  8. linux内核远程漏洞,Linux内核远程DoS漏洞 (CVE-2019-11477) 预警分析
  9. Win10镜像安装pytorch-gpu版
  10. 为何snapgene闪退?DNA序列无法显示?
  11. python100内的质数_python输出100以内的质数与合数实例代码
  12. 您的Android版本不兼容,android – “你的设备与此版本不兼容”
  13. 如何发送gmail邮件_如何从R和Gmail发送电子邮件
  14. 伊利洛伊大学厄巴纳-香槟分校计算机专业,伊利诺伊大学厄巴纳香槟分校信息管理专业怎么样?...
  15. 《可复制的领导力》读书笔记
  16. 在Excel中如何把每三行数据合并为一行?
  17. VB.net中金额大写转换
  18. C++手敲Roberts_Prewitt_Sobel实现阈值分割
  19. 比较两个结构体是否相等
  20. Unity—2D跑酷类游戏(一)

热门文章

  1. theHarvester使用
  2. 综述:视频和图像去雾算法以及相关的图像恢复和增强研究
  3. 加拿大移民政策利好,哪些人适合移民加拿大
  4. LeetCode第127题—单词接龙—Python实现
  5. matlab kdj线,kdj指标详什么时候买入,kdj金叉和死叉图解
  6. 基于深度学习的图像隐写分析综述 阅读
  7. 小波科普文章精粹:看森林,也看树木
  8. Java【并发】面试题
  9. POJ - 3067
  10. 常用命令整理一(git、adb、vim、cmd、sh)