1. 程式人生 > >3681 Prison Break(狀態壓縮 + 最短路)

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; }