HDU 3681 Prison Break(状压DP + BFS)题解
题意:一张图,F是起点,Y是必须要到的点,D不能走,G可以充电。可以往四个方向走,每走一步花费一个电,走到G可以选择充满电或者不充,每个G只能充一次。问你走遍Y的最小初始点亮。number(G) + number(Y) <= 15
思路:显然Y和G都只要用一次就行,那么状压YG状态。然后BFS出任意YG两点最短路,状压DP。用&判断最终结果是不是当前状态的子集。
代码:
#include<set> #include<map> #include<cmath> #include<queue> #include<cstdio> #include<vector> #include<cstring> #include <iostream> #include<algorithm> using namespace std; typedef long long ll; typedef unsigned long long ull; const int maxn = 15; const int M = maxn * 30; const ull seed = 131; const int INF = 0x3f3f3f3f; const int MOD = 1e4 + 7; int dp[1 << maxn][maxn]; char mp[maxn + 10][maxn + 10]; int n, m, sx, sy; int cnt; int id[maxn * maxn + 20]; int bettery[maxn * maxn + 20]; int getid(int x, int y){ return id[x * m + y]; } struct Node{ int x, y, step; }; int dis[maxn * maxn + 10][maxn * maxn + 10]; int vis[maxn + 5][maxn + 5]; int to[4][2] = {0, 1, 0, -1, 1, 0, -1, 0}; void bfs(int x, int y){ memset(vis, 0, sizeof(vis)); Node a, b; queue<Node> q; while(!q.empty()) q.pop(); vis[x][y] = 1; a.x = x, a.y = y, a.step = 0; q.push(a); while(!q.empty()){ a = q.front(); q.pop(); if(mp[a.x][a.y] != 'D' && mp[a.x][a.y] != 'S') dis[getid(x, y)][getid(a.x, a.y)] = a.step; for(int i = 0; i < 4; i++){ b.x = a.x + to[i][0]; b.y = a.y + to[i][1]; if(b.x < 0 || b.y < 0 || b.x >= n || b.y >= m) continue; if(vis[b.x][b.y]) continue; if(mp[b.x][b.y] == 'D') continue; vis[b.x][b.y] = 1; b.step = a.step + 1; q.push(b); } } } int Fin; bool check(int st){ memset(dp, -1, sizeof(dp)); for(int i = 0; i < cnt; i++){ if(dis[cnt][i] <= st){ if(bettery[i]) dp[1 << i][i] = st; else dp[1 << i][i] = st - dis[cnt][i]; } } for(int i = 0; i < (1 << cnt); i++){ for(int j = 0; j < cnt; j++){ if(dp[i][j] == -1) continue; if(!((1 << j) & i)) continue; if((i & Fin) == Fin) return true; for(int k = 0; k < cnt; k++){ if((1 << k) & i) continue; if(dis[j][k] > dp[i][j]) continue; dp[i | (1 << k)][k] = max(dp[i][j] - dis[j][k], dp[i | (1 << k)][k]); if(bettery[k]) dp[i | (1 << k)][k] = st; } } } return false; } int main(){ while(~scanf("%d%d", &n, &m) && n + m){ cnt = 0; Fin = 0; int tmp = 0; memset(bettery, 0, sizeof(bettery)); for(int i = 0; i < n; i++){ scanf("%s", mp[i]); for(int j = 0; j < m; j++){ if(mp[i][j] == 'F'){ sx = i, sy = j; } else if(mp[i][j] == 'G'){ bettery[cnt] = 1; id[i * m + j] = cnt++; } else if(mp[i][j] == 'Y'){ Fin += (1 << cnt); id[i * m + j] = cnt++; tmp++; } } } id[sx * m + sy] = cnt; if(tmp == 0){ printf("0\n"); continue; } memset(dis, INF, sizeof(dis)); for(int i = 0; i < n; i++){ for(int j = 0; j < m; j++){ if(mp[i][j] != 'D' && mp[i][j] != 'S') bfs(i, j); } } int l = 0, r = 1000000; int ans = -1; while(l <= r){ int m = (l + r) >> 1; if(check(m)){ r = m - 1; ans = m; } else l = m + 1; } printf("%d\n", ans); } return 0; }