HDU 3681 Prison Break(状压DP + 二分)

来源:互联网 发布:遗传算法 交叉概率 编辑:程序博客网 时间:2024/05/16 17:19

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3681


题意:给一张N * M的图,只能上下左右移动,每移动一格消耗一点能量,其中F是起点,D不能走,G点可以把能量加满,Y点必须经过,问最少需要多大的能量容量可以将Y点全部遍历


思路:先将原图hash成易处理的图,再bfs预处理出每个F,G,Y到其他点的最短距离,然后二分答案进行状压DP,其中dp[s][u]代表在当前状态s下且到达u点时还剩下的能量


#include <cstdio>#include <cstring>#include <queue>#include <stack>#include <functional>#include <utility>#include <iostream>#include <algorithm>#include <cstring>#include <string>#include <set>#include <cmath>#include <cstdlib>#include <climits>#define lson l, mid, rt << 1#define rson mid + 1, r, rt << 1 | 1#pragma comment (linker, "/STACK:1024000000,1024000000")using namespace std;typedef long long ll;typedef pair<int, int> P;const int maxn = 300;const int inf = 0x3f3f3f3f;int cnt = 0, head[maxn];struct edge{int from, to, nxt;} e[maxn * maxn];void init(){cnt = 0;memset(head, -1, sizeof(head));}void addedge(int u, int v){e[cnt].from = u;e[cnt].to = v;e[cnt].nxt = head[u];head[u] = cnt++;}int n, m, tot, st;int dis[20][20], id[20][20], dx[20], dy[20];char s[20][20];bool vis[maxn];int dp[1 << 16][16];void bfs(int s){memset(vis, 0, sizeof(vis));queue <P> que;P cur, next;cur.first = s, cur.second = 0;int sx = s / m, sy = s % m;que.push(cur);vis[s] = 1;while (!que.empty()){cur = que.front();que.pop();int u = cur.first, su = cur.second;int ux = u / m, uy = u % m;if (id[ux][uy] != -1)dis[id[sx][sy]][id[ux][uy]] = su;for (int i = head[u]; ~i; i = e[i].nxt){int v = e[i].to;if (!vis[v]){vis[v] = 1;next.first = v, next.second = su + 1;que.push(next);}}}}bool solve(int mid){memset(dp, -1, sizeof(dp));dp[1][0] = mid;for (int i = 0; i < (1 << tot); i++){for (int j = 0; j < tot; j++){if (dp[i][j] == -1 || !(i & (1 << j))) continue;if ((st & i) == st) return true;for (int k = 0; k < tot; k++){if (i & (1 << k)) continue;if (dis[j][k] == -1) continue;if (dp[i][j] >= dis[j][k]){if (dp[i | (1 << k)][k] == -1 || dp[i | (1 << k)][k] < dp[i][j] - dis[j][k])dp[i | (1 << k)][k] = dp[i][j] - dis[j][k];if (s[dx[k]][dy[k]] == 'G')dp[i | (1 << k)][k] = mid;}}}}return false;}int nx[4] = {1, 0, -1, 0};int ny[4] = {0, 1, 0, -1};int main(){while (~scanf("%d%d", &n, &m) && (n + m)){init();for (int i = 0; i < n; i++)scanf("%s", s[i]);st = 0, tot = 1;memset(id, -1, sizeof(id));for (int i = 0; i < n; i++){for (int j = 0; j < m; j++){char c = s[i][j];if (c == 'D') continue;if (c == 'F'){dx[0] = i, dy[0] = j;id[i][j] = 0;}else if (c == 'G'){dx[tot] = i, dy[tot] = j;id[i][j] = tot++;}else if (c == 'Y'){dx[tot] = i, dy[tot] = j;id[i][j] = tot++;st |= (1 << (tot - 1));}for (int k = 0; k < 4; k++){int xx = i + nx[k], yy = j + ny[k];if (xx < 0 || yy < 0 || xx >= n || yy >= m || s[xx][yy] == 'D') continue;addedge(i * m + j, xx * m + yy);}}}memset(dis, -1, sizeof(dis));for (int i = 0; i < tot; i++)bfs(dx[i] * m + dy[i]);// for (int i = 0; i < tot; i++)// {// for (int j = 0; j < tot; j++)// printf("%d ", dis[i][j]);// printf("\n");// }int l = 0, r = n * m * (tot - 1), ans = inf;while (l <= r){int mid = (l + r) >> 1;if (solve(mid))r = mid - 1, ans = min(ans, mid);elsel = mid + 1;}if (ans == inf) ans = -1;cout << ans << endl;}return 0;}


0 0