POJ 2946 字符串hash + BFS

76 篇文章 0 订阅
7 篇文章 0 订阅

题意就是给出一个地图。有一个起点,一个终点

我们要从起点到终点去。但是地上有毒液,不能走,不过有一些箱子可以供你跳来跳去。这些箱子的高度是2,3或者4

然后每次跳一步只能去4个方向相邻坐标的箱子。箱子之间不用管高度是多少,只要相邻了就可以跳上去

而且箱子可以放倒。也是4个方向,放倒后的长度就是之前的高度,但是放倒一次后就不能再放倒了。我们可以规定此时的高度为1。而且放倒必须是有确切的那么多空位才能放倒,如果前边有箱子挡着或者前面是终点挡着了,都不能放倒

然后问最少多少步能到终点,放倒箱子和跳箱子都算1步


这题我刚开始用了个很奇葩的5进制去表示每行的状态来节省空间,结果没判状态的重复,悲惨的TLE了

因为在BFS的过程中是能改变地图的,所以判状态是否以前出现过就比较麻烦,不过还算好写。

把整张地图作为一个字符串hash了,塞到一个表里,然后每次扩展状态的时候从表里看是否出现过,就可以了

#include <iostream>
#include <algorithm>
#include <cstring>
#include <string>
#include <cstdio>
#include <cmath>
#include <queue>
#include <map>
#include <set>
#define MAXN 100005
#define MAXM 200005
#define INF 1000000011
#define lch(x) x<<1
#define rch(x) x<<1|1
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define eps 1e-7
using namespace std;
int mod = 100003;
unsigned int Hash(char *str)
{
    unsigned int seed = 131; // 31 131 1313 13131 131313 etc..
    unsigned int hash = 0;
    while (*str) hash = hash * seed + (*str++);
    return (hash & 0x7FFFFFFF) % mod;
}
struct P
{
    int x, y, num, next;
    char s[70];
    P(){}
    P(int _x, int _y, int _num, char *_s){x = _x; y = _y; num = _num; strcpy(s, _s);}
}edge[MAXM * 2], q[MAXM + 5];
int head[MAXN], e;
int n, sx, sy, ex, ey;
char tmp[70];
char mp[70];
int xx[] = {1, 0, -1, 0};
int yy[] = {0, 1, 0, -1};
void init()
{
    memset(head, -1, sizeof(head));
    e = 0;
}
void add(int u, int x, int y, char *s)
{
    edge[e].x = x;
    edge[e].y = y;
    strcpy(edge[e].s, s);
    edge[e].next = head[u];
    head[u] = e++;
}
bool ok(int x, int y, char *s)
{
    if(x < 0 || y < 0 || x >= n || y >= n || s[x * n + y] == '.') return 0;
    int u = Hash(s);
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        if(edge[i].x == x && edge[i].y == y && strcmp(s, edge[i].s) == 0) return 0;
    }
    return 1;
}
void bfs()
{
    int h = 0, t = 0;
    if(ok(sx, sy, mp))
    {
        q[t++] = P(sx, sy, 0, mp);
        add(Hash(mp), sx, sy, mp);
    }
    int ans = -1;
    while(h != t)
    {
        P tp = q[h++];
        if(h >= MAXM) h -= MAXM;
        if(tp.x == ex && tp.y == ey)
        {
            ans = tp.num;
            break;
        }
        for(int i = 0; i < 4; i++)
        {
            int tx = tp.x + xx[i];
            int ty = tp.y + yy[i];
            if(ok(tx, ty, tp.s))
            {
                q[t++] = P(tx, ty, tp.num + 1, tp.s);
                if(t >= MAXM) t -= MAXM;
                add(Hash(tp.s), tx, ty, tp.s);
            }
        }
        for(int i = 0; i < 4; i++)
        {
            for(int j = 0; j < n * n; j++) tmp[j] = tp.s[j];
            tmp[n * n] = '\n';
            int k = tmp[tp.x * n + tp.y] - '0';
            if(k == 1) continue;
            int tx = tp.x, ty = tp.y;
            int flag = 0;
            for(int j = 0; j < k; j++)
            {
                tx += xx[i];
                ty += yy[i];
                if(tx < 0 || ty < 0 || tx >= n || ty >= n || tmp[tx * n + ty] != '.') flag = 1;
            }
            if(flag) continue;
            tx = tp.x, ty = tp.y;
            tmp[tx * n + ty] = '.';
            for(int j = 0; j < k; j++)
            {
                tx += xx[i];
                ty += yy[i];
                tmp[tx * n + ty] = '1';
            }
            tx = tp.x + xx[i], ty = tp.y + yy[i];
            if(ok(tx, ty, tmp))
            {
                q[t++] = P(tx, ty, tp.num + 1, tmp);
                if(t >= MAXM) t -= MAXM;
                add(Hash(tmp), tx, ty, tmp);
            }
        }
    }
    if(ans == -1) printf("Impossible.\n");
    else printf("%d\n", ans);
}
int main()
{
    while(scanf("%d%d%d", &n, &sx, &sy) != EOF)
    {
        if(!n && !sx && !sy) break;
        sx--;
        sy--;
        init();
        for(int i = 0; i < n; i++)
        {
            scanf("%s", tmp);
            for(int j = 0; j < n; j++)
            {
                mp[i * n + j] = tmp[j];
                if(tmp[j] == 'E') mp[i * n + j] = '1', ex = i, ey = j;
            }
        }
        mp[n * n] = '\0';
        bfs();
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值