洛谷P1979 华容道(dfs)
来源:互联网 发布:淘宝开店模板 编辑:程序博客网 时间:2024/05/21 10:23
由于不太会打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); }}
阅读全文
0 0
- 洛谷P1979 华容道(dfs)
- 洛谷 P1979 华容道
- 洛谷 P1979 华容道
- [洛谷luogu] P1979 [NOIP2013T6]华容道
- P1979 华容道
- [P1979]华容道
- [P1979]华容道
- P1979 [NOIP]华容道
- NOIP2013 D2T3 codevs 3290 洛谷 P1979 华容道 题解报告
- P1979 [NOIP]华容道(70分的暴力)
- 【NOIP2013】洛谷1979 华容道
- 华容道(Java Applet)
- 华容道
- 华容道
- 华容道
- 华容道
- 华容道!
- 华容道
- [设计模式]2. Factory Method
- 15.3.2 OutputStream和Writer
- 图像的绘制 直线 椭圆 圆形 多边形
- 自律之苦
- 菜鸟学数据库(四)——超键、候选键、主键、外键
- 洛谷P1979 华容道(dfs)
- python基础-打开模式、seek、修改文件、with上下文管理、truncate
- Redis keys 命令
- 红黑树(RB-tree)比AVL树的优势在哪?
- BZOJ 2744 浅谈异或二进制分析及二分图最大团
- array_diff 数组差集
- SSM整合的一些配置(基于Maven工程,逆向工程,Restful风格)
- leetcode解题方案--024--Swap Nodes in Pairs
- Struts2_012_Struts中文处理