题目链接: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);
else
l = mid + 1;
}
if (ans == inf) ans = -1;
cout << ans << endl;
}
return 0;
}