BZOJ 1556: 墓地秘密(SPFA+状压DP)

题面

这里写图片描述
这里写图片描述

权限门


思路

黄学长的题,比较简单的状压DP,考试时写了一个多小时,然后因为一个小错误只拿了40分。

比较明显的是,只有机关墙四周的点是有用的, 有用的点只有4*K个。

我们记DP[S][i][d]表示到过S集合,最后碰到的机关墙是i,停在墙的方向为d。然后通过枚举下一步去哪里、什么方向可以进行转移。

提前预处理出4*K个点两两之间的“转向”最短路,这个通过bfsSPFA可以搞。我个人偏向写SPFA。

细节不多,注意处理格子出界或是障碍的情况即可。

总时间复杂度:O(预处理+2^K*(4K)^2)。

考试时的错误:一开始机器人的方向随便定了一个,但是应该搞出四个才对。(考试时脑子比较不好使,代码也长得不像话)


代码

#include <bits/stdc++.h>
#define maxL 102
#define maxn 16
#define INF 0x7FFFFFFF
#define ALL ((1 << K) - 1)

using namespace std;

int n, m, K, ans = INF, Tx, Ty;
char M[maxL][maxL];
const int dx[] = {0, 0, 1, -1};
const int dy[] = {-1, 1, 0, 0};
const int dddd[] = {1, 3, 0, 2};

struct Key{
    int x, y;
}w[maxn];

struct Data{
    int x, y, d;
    Data() {}
    Data(int _x, int _y, int _d):x(_x), y(_y), d(_d) {}
}Q[maxL*maxL];

int Dis[4][maxL][maxL], D[maxn][4][4][maxL][maxL];
bool Vis[4][maxL][maxL];

int Getx(int S, int d){
    if(d == 0)  return w[S].x - 1;
    else if(d == 1)  return w[S].x;
    else if(d == 2)  return w[S].x + 1;
    return w[S].x;
}

int Gety(int S, int d){
    if(d == 0) return w[S].y;
    else if(d == 1)  return w[S].y + 1;
    else if(d == 2)  return w[S].y;
    return w[S].y - 1;
}

void SPFA(int S, int ddd){

    int sx = Getx(S, ddd), sy = Gety(S, ddd);

    if(sx < 1 || sy < 1 || sx > n || sy > m || M[sx][sy-1] == '#')  return;

    for(int d = 0; d < 4; d++)
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
                Dis[d][i][j] = INF, Vis[d][i][j] = false;

    Dis[ddd][sx][sy] = 0;
    Vis[ddd][sx][sy] = true;

    int hh = 0, tt = 0, mm = maxL * maxL;
    Q[hh] = Data(sx, sy, ddd);

    while(hh <= tt){
        Data now = Q[hh%mm];
        hh ++;
        int xx = now.x, yy = now.y, d = now.d;

        for(int dd = 0; dd < 4; dd++){
            if(d == dd)  continue;
            if(Dis[dd][xx][yy] > Dis[d][xx][yy] + 1){
                Dis[dd][xx][yy] = Dis[d][xx][yy] + 1;
                if(!Vis[dd][xx][yy]){
                    Vis[dd][xx][yy] = true;
                    tt ++;
                    Q[tt%mm] = Data(xx, yy, dd);
                }
            }
        }

        for(int i = 0; i < 4; i++){
            int nx = xx + dx[i], ny = yy + dy[i], nd = dddd[i];
            if(nx < 1 || ny < 1 || nx > n || ny > m || M[nx][ny-1] == '#')  continue;
            if(d == nd && Dis[nd][nx][ny] > Dis[d][xx][yy]){
                Dis[nd][nx][ny] = Dis[d][xx][yy];
                if(!Vis[nd][nx][ny]){
                    Vis[nd][nx][ny] = true;
                    tt ++;
                    Q[tt%mm] = Data(nx, ny, nd);
                }
            }
        }
        Vis[d][xx][yy] = false;
    }

    memcpy(D[S][ddd], Dis, sizeof(Dis));
}

int DP[1<<maxn][maxn][4];

int main(){

    freopen("maze.in", "r", stdin);
    freopen("maze.out", "w", stdout);

    scanf("%d%d%d", &n, &m, &K);

    for(int i = 1; i <= n; i++)  scanf("%s", M[i]);

    int a, b;
    for(int i = 1; i <= K; i++){
        scanf("%d%d", &a, &b);
        w[i].x = a;  w[i].y = b;
    }

    scanf("%d%d", &Tx, &Ty);

    for(int i = 1; i <= K; i++)
        for(int j = 0; j < 4; j++)  SPFA(i, j);

    for(int s = 0; s <= ALL; s++)
        for(int i = 1; i <= K; i++)
            for(int d = 0; d < 4; d++)
                DP[s][i][d] = INF;

    for(int d = 0; d < 4; d++){
        if(d == 0)  w[0].x = Tx + 1, w[0].y = Ty;
        else if(d == 1)  w[0].x = Tx, w[0].y = Ty - 1;
        else if(d == 2)  w[0].x = Tx - 1, w[0].y = Ty;
        else  w[0].x = Tx, w[0].y = Ty + 1;
        SPFA(0, d);
    }

    for(int d = 0; d < 4; d++)
        for(int i = 1; i <= K; i++){
            int ns = 1 << (i-1);
            for(int nd = 0; nd < 4; nd++){
                int xx = Getx(i, nd), yy = Gety(i, nd);
                if(xx < 1 || xx > n || yy < 1 || yy > m || M[xx][yy-1] == '#')  continue;
                if(D[0][d][nd][xx][yy] == INF)  continue;
                DP[ns][i][nd] = min(DP[ns][i][nd], D[0][d][nd][xx][yy] + 1);
            }
        }

    for(int s = 0; s < ALL; s++){
        for(int i = 1; i <= K; i++)
            for(int d = 0; d < 4; d++){
                if(DP[s][i][d] == INF)  continue;
                for(int j = 1; j <= K; j++){
                    if((1<<(j-1)) & s)  continue;
                    int ns = s | (1 << (j-1));
                    for(int nd = 0; nd < 4; nd++){
                        int xx = Getx(j, nd), yy = Gety(j, nd);
                        if(xx < 1 || xx > n || yy < 1 || yy > m || M[xx][yy-1] == '#')  continue;
                        if(D[i][d][nd][xx][yy] == INF)  continue;
                        DP[ns][j][nd] = min(DP[ns][j][nd], DP[s][i][d] + D[i][d][nd][xx][yy]);
                    }
                }
            }
    }

    for(int i = 1; i <= K; i++)
        for(int d = 0; d < 4; d++)
            ans = min(ans, DP[ALL][i][d]);

    printf("%d\n", ans);

    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值