poj3346
题意:给出一张地图矩阵,上面有些墙和石头,在空地上行走不需要时间,墙不可穿越,石头可以慢慢打破也可以用工具打破,用工具打破不需要时间,慢慢打破需要相应的时间。每个入口对应不同的携带工具的数量,且只能从一个入口进入,再不能去别的入口。问要拿到宝藏需要的最少时间。
分析:带堆优化的最短路。建图比较复杂,图中每个节点对应矩阵中的一个格的一种工具状态,矩阵中走到每个格身上可能剩余的工具数量为0~26个,因此矩阵中每个格对应最短路图中的27个节点,每个节点有两个属性一个是地图中的位置,一个是剩余工具的数量。下面向图中加边,矩阵中a,b两格相邻,如果a是石头,那么我们由节点(a,k)向节点(b,k)引边(k=0~26),权值为破坏a石头需要消耗的时间。我们再由节点(a,k+1)向节点(b,k)引边,权值为0。如果a不是石头那么我们由节点(a,k)向节点(b,k)引边(k=0~26),权值为0。要对矩阵中所有不是墙的相邻点进行加边操作,包括入口。然后我们加入一个超级源,由超级源向各个入口对应的相应节点引边。例如:入口a有x个工具,那么我们由超级源向边(a,x)引一条权值为0的边。然后我们只需要用带有堆优化dijkstra来求超级源到宝藏的最短距离即可。这样建图相当于把在遇到一个石头时候的两种选择,变成了两条岔路,两条路也分别对应了不同选择所需要消耗的时间。相当于我们把矩阵看成一个三维空间,一座大楼,前两维就是原矩阵的两维,而第三维是高度,对应了改格剩余的工具数量。然后从每块石头的位置可以不花时间走到其下一层的四方向相邻格,也可以花费一定的时间走到同层的相邻格。超级源是在地面的一个跳板,可以跳到各个入口对应的大楼的隔间中,我们的目的是从超级源用最快的速度走入大楼的宝藏对应的柱状空间中。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <algorithm> #include <queue> using namespace std; #define maxn 105 #define inf 0x3f3f3f3f struct Point { int x, y; Point() { } Point(int xx, int yy) : x(xx), y(yy) { } } q[maxn * maxn], point[maxn * maxn]; struct Edge { Point v; int next, w; } edge[maxn * maxn * maxn * 10]; struct Elem { int w; Point v; Elem() { } Elem(int ww, Point pp) : w(ww), v(pp) { } }; priority_queue<Elem> pq; int dist[maxn * maxn][30]; char map[maxn][maxn]; int n, m; bool vis1[maxn * maxn][30]; int head[maxn * maxn][30]; int cnt, idcnt; int id[maxn][maxn]; int dir[4][2] = { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 } }; bool operator <(const Elem &a, const Elem &b) { return a.w > b.w; } int wall(Point &a) { if (map[a.x][a.y] <= '9' && map[a.x][a.y] >= '1') return map[a.x][a.y] - '0'; return 0; } int packnum(char ch) { if (ch == '#') return 0; if (isupper(ch)) return ch - 'A' + 1; return -1; } bool ok(Point &a) { if (a.x < 0 || a.y < 0 || a.x >= n || a.y >= m) return false; if (map[a.x][a.y] == '*') return false; if (packnum(map[a.x][a.y]) != -1) return false; return true; } void addedge1(Point a, Point b, int w) { edge[cnt].v = b; edge[cnt].w = w; edge[cnt].next = head[a.x][a.y]; head[a.x][a.y] = cnt++; } int getid(Point a) { if (~id[a.x][a.y]) return id[a.x][a.y]; id[a.x][a.y] = idcnt++; point[id[a.x][a.y]] = a; return id[a.x][a.y]; } void addedge(Point &a, Point &b) { int x = getid(a); int y = getid(b); if (wall(a)) for (int i = 0; i < 26; i++) addedge1(Point(x, i + 1), Point(y, i), 0); for (int i = 0; i <= 26; i++) addedge1(Point(x, i), Point(y, i), wall(a)); } void bfs(Point a) { getid(a); for (int i = 0; i < 4; i++) { Point b(a.x + dir[i][0], a.y + dir[i][1]); if (ok(b)) addedge(a, b); } } void make_graph() { memset(head, -1, sizeof(head)); memset(id, -1, sizeof(id)); cnt = 0; idcnt = 1; for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) if (map[i][j] != '*') { bfs(Point(i, j)); if (~packnum(map[i][j])) addedge1(Point(0, 0), Point(getid(Point(i, j)), packnum(map[i][j])), 0); } } void dijkstra() { memset(vis1, 0, sizeof(vis1)); memset(dist, -1, sizeof(dist)); dist[0][0] = 0; pq.push(Elem(0, Point(0, 0))); while (!pq.empty()) { Elem x; do { x = pq.top(); pq.pop(); } while (!pq.empty() && vis1[x.v.x][x.v.y]); if (vis1[x.v.x][x.v.y]) break; Point u = x.v; vis1[u.x][u.y] = true; for (int i = head[u.x][u.y]; ~i; i = edge[i].next) { Point v = edge[i].v; if (vis1[v.x][v.y]) continue; if (dist[v.x][v.y] == -1 || dist[v.x][v.y] > dist[u.x][u.y] + edge[i].w) { dist[v.x][v.y] = dist[u.x][u.y] + edge[i].w; pq.push(Elem(dist[v.x][v.y], v)); } } } int ans = inf; for (int i = 0; i < n; i++) for (int j = 0; j < m; j++) if (map[i][j] == '$') { int x = id[i][j]; for (int k = 0; k <= 26; k++) if (~dist[x][k]) ans = min(ans, dist[x][k]); } if (ans == inf) printf("IMPOSSIBLE\n"); else printf("%d\n", ans); } int main() { //freopen("t.txt", "r", stdin); while (gets(map[0]), strcmp(map[0], "--")) { int x = 1; while (gets(map[x]), map[x][0]) x++; n = x; m = strlen(map[0]); make_graph(); dijkstra(); } return 0; }