hdu3681--Prison Break(TSP+二分)
好难的一道题。
题意:一个机器人要逃出监狱,每走一步消耗一点电量,初始时电量是满的。给一个n*m(n,m<=15)的字符数组代表监狱,F代表起始点,G代表补充满电量,每个G只能补充一次,Y代表开关,D不能经过,S表示空地。要求打开所有开关,也就是经过所有Y点,电池的满电量最少是多少。如果不能逃出输出-1。G和Y的个数和不会超过15。
题解:二分答案。通过bfs预处理出G,Y,F两两之间的距离,然后转化成TSP求解。借鉴了别人的代码。
#include <bits/stdc++.h> #define clr(x,c) memset(x,c,sizeof(x)) using namespace std; struct Point { int x, y; int d; Point(int x, int y, int d) : x(x), y(y), d(d) {} Point(int x, int y) : x(x), y(y), d(0) {} Point() {} bool operator ==(const Point a) const { if (x == a.x && y == a.y) return true; return false; } } p[50]; int m, n; char mp[20][20]; int cf; int dis[20][20]; int cnt = 0; int ac = 0; // 所有y的集合 int dir[4][2] = {0, 1, 0, -1, 1, 0, -1, 0}; int vis[20][20]; int dp[1<<17][20]; bool ok(int x, int y) { if (x < n && x >= 0 && y < m && y >= 0 && mp[x][y] != 'D' && !vis[x][y]) return true; return false; } int bfs(int a, int b) { clr(vis, 0); queue<Point> q; q.push(p[a]); vis[p[a].x][p[a].y] = 1; while (!q.empty()) { Point now = q.front(); q.pop(); if (now == p[b]) return now.d; for (int i = 0; i < 4; ++i) { int nx = now.x + dir[i][0]; int ny = now.y + dir[i][1]; if (!ok(nx, ny)) continue; int nd = now.d + 1; q.push(Point(nx, ny, nd)); vis[nx][ny] = 1; } } return -1; } void getDis() { for (int i = 0; i < cnt; ++i) { for (int j = i; j < cnt; ++j) { if (i == j) dis[i][j] = 0; else dis[j][i] = dis[i][j] = bfs(i, j); } } } bool canGo(int en) { clr(dp, -1); int st = (1 << cnt); dp[1 << cf][cf] = en; for (int i = 0; i < st; ++i) { for (int j = 0; j < cnt; ++j) { if ( !((1 << j) & i) || dp[i][j] == -1 ) continue; if ((i & ac) == ac) return true; for (int k = 0; k < cnt; ++k) { if ( (1 << k) & i || dis[j][k] == -1 || dp[i][j] < dis[j][k] ) continue; int nt = (1 << k) | i; dp[nt][k] = max(dp[nt][k], dp[i][j] - dis[j][k]); if (mp[ p[k].x ][ p[k].y ] == 'G') dp[nt][k] = en; } } } return false; } int main() { while (~scanf("%d%d", &n, &m)) { if (n == 0 && m == 0) break; for (int i = 0; i < n; ++i) scanf("%s", mp[i]); ac = cnt = 0; for (int i = 0; i < n; ++i) { for (int j = 0; j < m; ++j) { if (mp[i][j] == 'F') { cf = cnt; ac += (1 << cnt); p[cnt++] = Point(i, j); } else if (mp[i][j] == 'G') { p[cnt++] = Point(i, j); } else if (mp[i][j] == 'Y') { ac += (1 << cnt); p[cnt++] = Point(i, j); } } } getDis(); int l = 0, r = 300; while (l <= r) { int mid = (l + r) >> 1; if (canGo(mid)) r = mid - 1; else l = mid + 1; } if (l >= 300) l = -1; printf("%d\n", l); } return 0; }