[UVa 1601] The Morning after Halloween (双向广搜)

来源:互联网 发布:java中get(class) 编辑:程序博客网 时间:2024/05/16 01:50

链接

UVa 1601


题意

类似“推箱子”,将n个小写字母推到对应的大写字母的位置。每一步对每个小写字母可推最多一次,且不可发生冲突。


题解

将“局势”看做状态,就是一道隐式图搜索,但是状态数比较多,时间上还是挺紧的。
由于有>1/4的格子都是障碍物,可以将可走的格子抽出单独建图,邻接表存储,再广搜即可。
在此基础上可以将算法改进为双向广搜,这样速度更快(实测快了有一倍)。

双向广搜有个坑,就是要正反向每次扩展一层(而不是一个节点),否侧会漏解。
为了防止退化,可以按照正向和反向的节点数量决定下一层扩展正向or反向(实测确实更快了)。


代码

#include <cstdio>#include <cstring>#include <algorithm>#include <iostream>#include <vector>#include <queue>using namespace std;char g[20][20];int w, h, n, st[3], ed[3], cnt, idx[20][20], locate[1<<8], dx[4] = { 1, -1, 0, 0 }, dy[4] = { 0, 0, 1, -1 };const int inf = 1e9 + 7;vector<int> to[20 * 20];void init(){    for(int i = 0; i < h; i++) gets(g[i]);    cnt = 0;    for(int i = 0, k = 0; i < h; i++)        for(int j = 0; j < w; j++)            if(g[i][j] != '#') {                idx[i][j] = ++cnt;                to[cnt].clear(); to[cnt].push_back(cnt);                if(isupper(g[i][j])) {                    locate[g[i][j]] = k++;                }            }    for(int i = 0; i < h; i++)        for(int j = 0; j < w; j++)            if(g[i][j] != '#') {                if(isupper(g[i][j])) {                    ed[locate[g[i][j]]] = idx[i][j];                }                if(islower(g[i][j])) {                    st[locate[toupper(g[i][j])]] = idx[i][j];                }                for(int k = 0, a, b; k < 4; k++) {                    a = i + dx[k], b = j + dy[k];                    if(a >= 0 && a < h && b >= 0 && b < w && g[a][b] != '#')                        to[idx[i][j]].push_back(idx[a][b]);                }            }    to[cnt + 1].clear(); to[cnt + 1].push_back(cnt + 1);    to[cnt + 2].clear(); to[cnt + 2].push_back(cnt + 2);    if(n == 1) { st[1] = ++cnt; ed[1] = st[1]; st[2] = ++cnt; ed[2] = st[2]; }    if(n == 2) { st[2] = ++cnt; ed[2] = st[2]; }}struct Node {    int v[3], d;    Node(int a, int b, int c, int _d) { v[0] = a, v[1] = b, v[2] = c; d = _d; }};queue<Node> que[2];int dis[2][1<<8][1<<8][1<<8], num[2];bool check(int a, int b, int c, int i, int j, int k){    if(a == b || b == c || c == a) return false;    if(b == i && a == j || b == k && c == j || c == i && a == k) return false;    return true;}int bfs(){    int ans = inf;    memset(dis, -1, sizeof(dis));    while(!que[0].empty()) que[0].pop(); num[0] = 1;    while(!que[1].empty()) que[1].pop(); num[1] = 1;    que[0].push(Node(st[0], st[1], st[2], 0)); dis[0][st[0]][st[1]][st[2]] = 0;    que[1].push(Node(ed[0], ed[1], ed[2], 0)); dis[1][ed[0]][ed[1]][ed[2]] = 0;    while(!que[0].empty() && !que[1].empty()) {        int x = int(num[0] > num[1]), y = int(num[0] <= num[1]);        int dep = que[x].front().d;        while(!que[x].empty() && que[x].front().d == dep)        {            Node now = que[x].front(); que[x].pop();            if(dis[y][now.v[0]][now.v[1]][now.v[2]] >= 0) {                return now.d + dis[y][now.v[0]][now.v[1]][now.v[2]];            }            for(int i = 0, a; i < to[now.v[0]].size(); i++) {                a = to[now.v[0]][i];                for(int j = 0, b; j < to[now.v[1]].size(); j++) {                    b = to[now.v[1]][j];                    for(int k = 0, c; k < to[now.v[2]].size(); k++) {                        c = to[now.v[2]][k];                        if(check(a, b, c, now.v[0], now.v[1], now.v[2]) && dis[x][a][b][c] < 0)                        {                            dis[x][a][b][c] = now.d + 1;                            que[x].push(Node(a, b, c, now.d + 1)); num[x]++;                        }                    }                }            }        }    }    return -1;}int main(){    //freopen("in.txt", "r", stdin);    while(cin >> w >> h >> n)    {        if(w + h + n == 0) break;        while(getchar() != '\n') continue;        init();        cout << bfs() << endl;    }    return 0;}

这里写图片描述

0 0