HDU - 3681 Prison Break(状态压缩 + 最短路)

题目大意:有一个机器人想越狱,越狱的要求是将所有的电网开关关掉。现在给出一个地图,’S’表示空地,‘F‘表示起始地点,‘G‘表示充电池,‘D‘表示禁地,‘Y‘开关
充电池可以将机器人的电充满。机器人每走一格就需要耗掉1点能量,问机器人的起始能量至少要是多少才可以逃出监狱

解题思路:先将所有能连通的点连通起来,将充电池和开关抽象出来,压缩成一个状态
求出每个充电池和开关之间的两两间的最短距离,接着二分枚举机器人的初始电量,再暴力枚举机器人行动的每种情况(dfs)

这里已经求出了充电池和开关之间的最短距离了,那怎样判断经过了充电池是否要充电呢
因为枚举的时候,是只枚举机器人到充电池和开关的最短路径的,如果中途遇到了充电池,也就是说最短路的路途中有充电池,这种情况经过是不进行充电的
而如果是枚举的下一点是充电池的话,这种情况是直接充电的
这样就把经过充电池该不该充电的情况给考虑进去了

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
#define N 20
#define M 410
#define S 2010

char g[N][N];
int head[M], u[S], v[S],  Next[S], id[N][N], dis[N][N], x[N], y[N];
int n, m, num, cnt, final;

void add_edgs(int s, int e) {
    u[cnt] = s;
    v[cnt] = e;
    Next[cnt] = head[s];
    head[s] = cnt++;    
}

void init() {
    memset(head, -1, sizeof(head));
    memset(id, -1, sizeof(id));
    memset(dis, -1, sizeof(dis));
    cnt = 0; num = 1; final = 0;

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

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (g[i][j] == 'D')
                continue;
            else if (g[i][j] == 'F') {
                x[0] = i;
                y[0] = j;
                id[i][j] = 0;
            }
            else if (g[i][j] == 'G') {
                x[num] = i;
                y[num] = j;
                id[i][j] = num++;
            }
            else if (g[i][j] == 'Y') {
                x[num] = i;
                y[num] = j;
                final |= (1 << num);
                id[i][j] = num++;
            }

        if (i > 0 && g[i - 1][j] != 'D')
            add_edgs(i * m + j, (i - 1) * m + j);
        if (j > 0 && g[i][j - 1] != 'D')
            add_edgs(i * m + j, i * m + j - 1);
        if (i + 1 < n && g[i + 1][j] != 'D')
            add_edgs(i * m + j, (i + 1)* m + j);
        if (j + 1 < m && g[i][j + 1] != 'D')
            add_edgs(i * m + j, i * m + j + 1);
        }
    }
}
struct Node {
    int pos, dis;
}n1, n2;

bool vis[M];
void bfs(int s) {
    queue<struct Node> q;
    memset(vis, 0, sizeof(vis));
    n1.pos = s;
    n1.dis = 0;
    q.push(n1);
    vis[s] = 1;

    while (!q.empty()) {
        n2 = q.front();
        q.pop();
        int t = n2.pos;

        if (id[t / m][t % m] != -1)
            dis[id[t / m][t % m]][id[s / m][s % m]] = n2.dis;

        for (int i = head[t]; i != -1; i = Next[i]) {
            if (vis[v[i]])
                continue;
            n1.pos = v[i];
            n1.dis = n2.dis + 1;
            q.push(n1);
            vis[v[i]] = 1;
        }
    }
}

bool mark[N];
bool dfs(int pos, int state, int power, int all_power) {
    if ((state & final) == final)
        return true;

    for (int i = 1; i < num; i++) {
        if (mark[i] || dis[pos][i] == -1)
            continue;
        if (power >= dis[pos][i]) {
            if (g[x[i]][y[i]] == 'G') {
                mark[i] = 1;
                if (dfs(i, state | (1 << i), all_power, all_power))
                    return true;
                mark[i] = 0;
            }
            else {
                mark[i] = 1;
                if (dfs(i, state | (1 << i), power - dis[pos][i], all_power))
                    return true;
                mark[i] = 0;
            }
        }
    }
    return false;
}

void solve() {
    for (int i = 0; i < num; i++)
        bfs(x[i] * m + y[i]);

    bool flag = false;
    int ans = -1;
    for (int i = 1; i < num; i++)
        if (g[x[i]][y[i]] == 'Y' && dis[id[x[i]][y[i]]][0] == -1) {
            flag = true;
            break;
        }

    if (!flag) {
        int l = 0, r = n * m * (num - 1);
        while (l <= r) {
            int mid = (l + r) / 2;
            memset(mark, 0, sizeof(mark));
            mark[0] = 1;
            if (dfs(0, 1, mid, mid)) {
                ans = mid;
                r = mid - 1;
            }
            else {
                l = mid + 1;
            }
        }
    }
    printf("%d\n", ans);
}

int main() {
    while (scanf("%d%d", &n, &m) != EOF && n + m) {
        init();
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值