您的位置:首页 > 其它

hdu3681--Prison Break(TSP+二分)

2016-01-28 12:16 344 查看
好难的一道题。

题意:一个机器人要逃出监狱,每走一步消耗一点电量,初始时电量是满的。给一个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;
}


  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: