将起始点、终点和钥匙统一编号,预处理:

1.起始点到所有钥匙+终点的最短路

2.所有钥匙之间两两的最短路

3.所有钥匙到终点的最短路

将起始点和所有钥匙四方向出发设为起点BFS一遍,求出它到任意点任意方向的最短路dp[i][j][k]。(即到点(i,j)且方向为k的最短路),BFS时需要注意:

1.UDLR有可能组成死循环

2.转向符在墙边并且指向了墙时,无法重新选择方向。例如这个: S...L#E

3.只有豆腐块滑动碰到墙停止了之后,才能把这个点的坐标加入队列。在滑动过程中经过的点都不需要加入队列。

4.钥匙在墙边的情况。在一次滑动过程中,豆腐块经过K时只有一个方向。但是如果K在墙边,并且滑动方向可以使豆腐块在K的位置停住,那么在这个K的位置,豆腐块向四方向移动的状态都是可达的。

例如:S.....E......K#

在我的方法中,如果不特殊处理的话,会出现经过K时,只有向右的状态是可达的,其它方向都不可达。但是实际上,从K开始向上下左右的状态显然都是合法状态,都可以达到。

5.遇到强制转向符号的时候step是不会增加的,只有在墙边停止下来重新选择方向的时候step才+1

KtoK[i][j][x][y]表示起始点为 i 方向为x到终点为j方向为y的最短路

用二进制表示得到钥匙的状态,一共7把钥匙,2^7种状态。

ans[x][y][z]表示得到钥匙的状态为x,经过的最后一把钥匙为y且此时方向为z的最短路。

ans[x][y][z] = min( ans[ x ^ ( 1 << y ) ][ k ][ m ] + KtoK[k][y][m][z] );

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
using namespace std; const int MAXN = ;
const int INF = << ;
const int dx[] = { -, , , };//上下左右
const int dy[] = { , , -, }; struct Point
{
int x, y;
int move; //最小步数
int toward; //当前朝向
bool st;
Point( int x = , int y = , int mv = , int ct = , int to = , bool st = false ):
x(x), y(y), move(mv), toward(to), st(st) { }
}; int R, C;
int dp[MAXN][MAXN][];
int KtoK[][][][]; //钥匙i到钥匙j起始方向为x终止方向为y的最短路
int ans[ << ][][];
char mat[MAXN][MAXN];
int cntK; //钥匙个数
Point start, end, Key[]; void init()
{
for ( int i = ; i < ; ++i )
for ( int j = ; j < ; ++j )
for ( int m = ; m < ; ++m )
for ( int n = ; n < ; ++n )
KtoK[i][j][m][n] = INF; cntK = ;
for ( int i = ; i <= R; ++i )
for ( int j = ; j <= C; ++j )
{
switch ( mat[i][j] )
{
case 'S':
start.x = i, start.y = j;
break;
case 'E':
end.x = i, end.y = j;
break;
case 'K':
Key[cntK].x = i, Key[cntK].y = j, ++cntK;
break;
}
} Key[] = start;
Key[cntK] = end;
return;
} bool check( int x, int y )
{
return x > && x <= R && y > && y <= C;
} int BFS( Point st )
{
//printf("st(%d, %d)to%d ed(%d, %d)to%d\n", st.x, st.y, st.toward, ed.x, ed.y, ed.toward );
for ( int i = ; i < MAXN; ++i )
for ( int j = ; j < MAXN; ++j )
for ( int k = ; k < ; ++k )
dp[i][j][k] = INF;
dp[ st.x ][ st.y ][ st.toward ] = ; queue<Point> Q;
Q.push(st); while ( !Q.empty() )
{
Point p = Q.front();
Q.pop();
//printf("====%d %d\n", p.x, p.y ); int len = p.st ? p.toward : ;
for ( int i = p.st ? p.toward : ; i <= len; ++i )
{
int xx = p.x, yy = p.y;
if ( !check( xx + dx[i], yy + dy[i] ) ) continue;
if ( mat[ xx + dx[i] ][ yy + dy[i] ] == '#' ) continue; bool ok = true;
int toward = i;
while ( mat[xx][yy] != '#' )
{
//printf("%d %d %d\n", xx, yy, p.st );
switch( mat[xx][yy] )
{
case 'U':
toward = ;
break;
case 'D':
toward = ;
break;
case 'L':
toward = ;
break;
case 'R':
toward = ;
break;
default:
break;
}
xx += dx[toward];
yy += dy[toward];
if ( !check( xx, yy ) )
{
ok = false;
break;
}
int &res = dp[xx][yy][toward];
//printf( "%d dp[%d][%d][%d]=%d, %d\n", ok, xx, yy, toward, res, p.move + 1 );
if ( ok && p.move < res )
{
res = p.move;
if ( check( xx + dx[toward], yy + dy[toward] ) )
{
if ( mat[ xx + dx[toward] ][ yy + dy[toward] ] == '#' )
{
if ( mat[xx][yy] != 'L' && mat[xx][yy] != 'R' && mat[xx][yy] != 'U' && mat[xx][yy] != 'D' )
{
if ( mat[xx][yy] == 'K' ) //特殊处理K在墙边的情况
{
for ( int k = ; k < ; ++k )
dp[xx][yy][k] = min( dp[xx][yy][k], p.move + );
}
Q.push( Point( xx, yy, p.move + , , false ) );
}
}
}
}
else break; //之前少了这句话,一直TLE
}
}
}
return INF;
} int main()
{
//freopen("1006.in","r",stdin);
//freopen("out.txt","w",stdout);
while ( ~scanf( "%d%d", &R, &C ) )
{
for ( int i = ; i <= R; ++i )
scanf( "%s", &mat[i][] ); init(); //预处理所有钥匙之间的最短路
for ( int i = ; i < cntK; ++i )
{
for ( int k = ; k < ; ++k )
{
Point st = Key[i];
st.move = ;
st.st = true;
st.toward = k;
BFS( st );
for ( int j = ; j <= cntK; ++j )
for ( int m = ; m < ; ++m )
{
if ( i == j && k == m )
{
KtoK[i][j][k][m] = ;
continue;
}
KtoK[i][j][k][m] = dp[ Key[j].x ][ Key[j].y ][m];
//printf( "KtoK[%d][%d][%d][%d] = %d\n", i, j, k, m, KtoK[i][j][k][m] );
}
}
} for ( int i = ; i < ( << ( cntK - ) ); ++i )
for ( int j = ; j < cntK; ++j )
for ( int k = ; k < ; ++k ) ans[i][j][k] = INF; for ( int i = ; i < ; ++i ) ans[][][i] = ; for ( int i = ; i < ( << ( cntK - ) ); ++i )
for ( int j = ; j < cntK; ++j )
for ( int k = ; k < ; ++k )
{
int v = ans[i][j][k];
if ( v == INF ) continue;
for ( int y = ; y < cntK; ++y )
for ( int z = ; z < ; ++z )
{
int x = ( i | ( << ( y - ) ) );
if ( ans[x][y][z] > v + KtoK[j][y][k][z] )
ans[x][y][z] = v + KtoK[j][y][k][z];
}
} int aa = INF;
for ( int i = ; i < cntK; ++i )
for ( int j = ; j < ; ++j )
for ( int k = ; k < ; ++k )
{
aa = min( aa, ans[ ( << ( cntK - ) ) - ][i][j] + KtoK[i][cntK][j][k] );
}
if ( aa >= INF ) aa = -;
printf( "%d\n", aa );
}
return ;
}

HDU 4634 Swipe Bo 状态压缩+BFS最短路的更多相关文章

  1. hdu 4634 Swipe Bo bfs+状态压缩

    题目链接 状态压缩记录当前拿到了哪些钥匙, 然后暴力搜索. 搞了好几个小时, 一开始也不知道哪里错了, 最后A了也不知道一开始哪里有问题. #include <iostream> #inc ...

  2. HDU 4634 Swipe Bo (2013多校4 1003 搜索)

    Swipe Bo Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Su ...

  3. hdu 4634 Swipe Bo 搜索

    典型的bfs模拟 (广度优先搜索) ,不过有好多细节要注意,比如图中如果是  R#  走到这个R的话就无限往右走了,这样就挂了~肯定到不了出口.还有一种容易造成死循环的,比如 #E## DLLL D. ...

  4. hdu 3681 Prison Break(状态压缩+bfs)

    Problem Description Rompire . Now it’s time to escape, but Micheal# needs an optimal plan and he con ...

  5. 【bzoj1195】[HNOI2006]最短母串 AC自动机+状态压缩+BFS最短路

    原文地址:http://www.cnblogs.com/GXZlegend/p/6825226.html 题目描述 给定n个字符串(S1,S2,„,Sn),要求找到一个最短的字符串T,使得这n个字符串 ...

  6. 胜利大逃亡(续)(状态压缩bfs)

    胜利大逃亡(续) Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total S ...

  7. hdu 4352 数位dp + 状态压缩

    XHXJ's LIS Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total ...

  8. HDU 5025 Saving Tang Monk 【状态压缩BFS】

    任意门:http://acm.hdu.edu.cn/showproblem.php?pid=5025 Saving Tang Monk Time Limit: 2000/1000 MS (Java/O ...

  9. 【HDU - 1429】胜利大逃亡(续) (高级搜索)【状态压缩+BFS】

    Ignatius再次被魔王抓走了(搞不懂他咋这么讨魔王喜欢)…… 这次魔王汲取了上次的教训,把Ignatius关在一个n*m的地牢里,并在地牢的某些地方安装了带锁的门,钥匙藏在地牢另外的某些地方.刚开 ...

随机推荐

  1. Noip2015总结

    Noip2015战役总结 [游记部分] Day0 考前说是可以放松一下,下午呢就在机房打了几盘杀,一起玩了玩狼人.不过晚上觉得还是要有点氛围了,于是稍稍打了几个模板,觉得正确率还不错,给自己一点自信的 ...

  2. 快速幂取模 分类: ACM TYPE 2014-08-29 22:01 95人阅读 评论(0) 收藏

    #include<stdio.h> #include<stdlib.h> //快速幂算法,数论二分 long long powermod(int a,int b, int c) ...

  3. Sprite Kit 入门教程

    Sprite Kit 入门教程  Ray Wenderlich on September 30, 2013 Tweet 这篇文章还可以在这里找到 英语, 日语 If you're new here, ...

  4. rivers ioi2005 树形dp

    说句实话,写完这道题,很想吐一口血出来,以示我心情的糟糕: 题目很简单,树形dp,正常做30分钟,硬是做了好几个小时,真是伤心. 题解不写了,只是吐个槽,网上没有用背包写的dp,全是左儿子右兄弟写法, ...

  5. centos 安装软件

    1)一种是软件的源代码,您需要自己动手编译它.这种软件安装包通常是用gzip压缩过的tar包(后缀为.tar.gz).2)另一种是软件的可执行程序,你只要安装它就可以了.这种软件安装包通常被是一个RP ...

  6. mysql if条件

    #if表达式 SELECT reg_no, IF(reg_no='718170554','黄色宾利','红色宾利') FROM car WHERE reg_no IN ('718170554','12 ...

  7. 用linux服务器下的/dev/shm/来释放磁盘的压力

    巧用linux服务器下的/dev/shm/来释放磁盘的压力 浏览:646 | 更新:2013-06-18 18:08 | 标签: 磁盘 tmpfs是Linux/Unix系统上的一种基于内存的文件系统. ...

  8. POJ 1001 Exponentiation(JAVA,BigDecimal->String)

    题目 计算实数a的n次方,具体输出格式看案例 import java.util.*; import java.math.*; public class Main { public static voi ...

  9. iOS验证码倒计时(GCD实现)

    + (void)verificationCode:(void(^)())blockYes blockNo:(void(^)(id time))blockNo { __block ; //倒计时时间 d ...

  10. .Net知识点总结(一)

    1.文件上传:Jquery.uploadify  它依赖于flash  舍去起上传   功能  改用SWFupload  他是第三方的插件 2.验证码激活的时候,邮箱开始是写死的,但是为了以后更改邮箱 ...