Codeforces 676D Theseus and labyrinth

题目链接:http://codeforces.com/problemset/problem/676/D


题意:一个n×m的地图,每个房间向四个方向可能会有门,给定起点和终点,求可以到达终点的最小操作数。每次操作可以将所有房间的门顺时针旋转90,或者可以到达相邻的房间(两个房间之间必须都有门)。


思路:bfs搜索。flag[i][j][k]表示(i,j)点上转向k次的状态是否已经出现过。can[i][j][k]表示(i,j)房间的k方向上是否有门。对于旋转操作,我们只需要记录转的次数x即可。如果当前的枚举的前进方向为k,那么实际在数组中判断的位置就是(k+3*x)%4。


#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;

#define rep(i,j,k) for (int i=j;i<=k;i++)
#define Rrep(i,j,k) for (int i=j;i>=k;i--)
#define Clean(x,y) memset(x,y,sizeof(x))

const int dx[4] = {0,1,0,-1}; //R D L U
const int dy[4] = {1,0,-1,0};
const int maxn = 1009;

int n,m;
bool can[maxn][maxn][4]; //() 方向上是否有门 R D L U
bool flag[maxn][maxn][4];//() 转向次数
int stx,sty,edx,edy;

queue<int> x,y,step,turn;

void init()
{
    char c;
    Clean(can,false);
    Clean(flag,false);
    scanf("%d %d",&n,&m);
    rep(i,1,n)
    {
        getchar();
        rep(j,1,m)
        {
            c = getchar();
            if ( c == '+' )
                can[i][j][0] = can[i][j][1] = can[i][j][2] = can[i][j][3] = true;
            else if ( c == '-' ) can[i][j][0] = can[i][j][2] = true;
            else if ( c == '|' ) can[i][j][1] = can[i][j][3] = true;
            else if ( c == '^' ) can[i][j][3] = true;
            else if ( c == '>' ) can[i][j][0] = true;
            else if ( c == '<' ) can[i][j][2] = true;
            else if ( c == 'v' ) can[i][j][1] = true;
            else if ( c == 'L' ) can[i][j][0] = can[i][j][1] = can[i][j][3] = true;
            else if ( c == 'R' ) can[i][j][2] = can[i][j][1] = can[i][j][3] = true;
            else if ( c == 'U' ) can[i][j][0] = can[i][j][1] = can[i][j][2] = true;
            else if ( c == 'D' ) can[i][j][0] = can[i][j][2] = can[i][j][3] = true;
        }
    }
    scanf("%d%d%d%d",&stx,&sty,&edx,&edy);
}


bool check(int x,int y)
{
    return ( x >= 1 && x <= n && y >= 1 && y <= m );
}
void solve()
{

    x.push(stx);
    y.push(sty);
    step.push(0);
    turn.push(0);
    flag[stx][sty][0] = true;

    while( !x.empty() )
    {
        int tx = x.front();
        int ty = y.front();
        int ts = step.front();
        int tn = turn.front();

        x.pop(),y.pop(),step.pop(),turn.pop();

        if ( tx == edx && ty == edy )
        {
            printf("%d\n",ts);
            return;
        }

        int nx,ny,nn;

        rep(k,0,3)
        {
            nx = tx + dx[k];
            ny = ty + dy[k];

            int nextk = ( k == 0 || k == 2 )?2-k:4-k;

            if ( check(nx,ny) && !flag[nx][ny][tn] && can[tx][ty][(k+3*tn)%4] && can[nx][ny][ (nextk+3*tn)%4 ]   ) //位置合法 状态未出现 且两个房间之间互相有门
            {
                flag[nx][ny][tn] = true;
                x.push(nx);
                y.push(ny);
                step.push(ts+1);
                turn.push(tn);
            }
        }
        nn = ( tn + 1 ) % 4;//转一次
        if ( !flag[tx][ty][nn] )
        {
            flag[tx][ty][nn] = true;
            x.push(tx);
            y.push(ty);
            step.push(ts+1);
            turn.push(nn);
        }
    }
    puts("-1");
}
int main()
{
    init();
    solve();
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值