zoj3652 Maze
题意:给定一个n * m 的矩阵(迷宫),-1 表示该位置不能通过, 1 ~ 5 表示该位置被第i个怪控制, 0 表示该位置不被任何怪控制
给定起点和终点, 问Celica 最少需要多少回合才能通过。当然,还有很多限制条件:
1)每一个回合, Celica 的初始行动值为L, 没移动一个位置,行动值-1, 当行动值减为0, 则开始下一个回合
2)一旦踏入怪的所在位置,则立即会将怪杀死,怪所控制的位置也就变为0了
3)当移动到一个怪所控制的位置,行动立即减为0
4)每到达一个位置,首先变化的是行动值, 这就意味着,如果踏入一个怪所在的位置,而且该位置是被怪所控制的情况下,那么行动值先变为0,再将怪杀死
5)怪不一定在它控制的位置
6)起点一定没有怪, 但可能被某一个怪控制
分析:除了弄清题意外,首先应该注意到的是数据范围, n <= 50, 这种地图略大, 且求的是最小值,一般用的是广搜。 当然,如果地图再小一点,也可能是用IDA*等搜索方法解决。
不过,我首先想到的是用IDA, 通过限制回合数,深搜来求得最小回合数。不过没想到更好的剪枝方法。 为什么没想到广搜呢?其实是因为每移动一步,地图可能改变,那每一个状态都要将整个地图保存么?这个问题一开始没
想通, 所以就没敲了。 后来突然想到,地图改变是因为可能是某些怪挂掉了,而且最多才五只怪。那么只要知道五只怪的存活情况,就知道当前的地图是怎么样的了。
另外要考虑的问题就是,状态的表示了,表示一个唯一的状态。对于这种迷宫问题,首先就是考虑一个点是否只能走一次? 不是的,试想一下,是否存在一种情况,经过某一个点去杀一个怪,杀玩完再经过这个点去终点却存在最优值呢?
答案是肯定的。所以,我们应该用vis[x][y][state] 标记经过该点时的状态, state 表示 五只怪的存活情况,如果第二次经过该点时state的值没变, 那么也就没必要将该点重新压入队列, 当然,这里说的是优先队列, 因为每一次的
代价并非相等的
zoj3652
#include<iostream> #include<algorithm> #include<stdio.h> #include<string.h> #include<stdlib.h> #include<vector> #include<queue> using namespace std; const int N = 50 + 10; int g[N][N]; struct node { int t, mob, state;//t 表示回合数, mob 表示当前回合剩余行动值 int x, y; node(){} node(int x, int y, int t, int mob, int state):x(x), y(y), t(t), mob(mob), state(state){} bool friend operator < (const node &a, const node &b) { if(a.t != b.t) return a.t > b.t; return a.mob < b.mob; } }; priority_queue<node> Q; int n, m, L; bool vis[N][N][32]; int mark[N][N];//标记该位置存在哪一个怪,-1表示不存在怪 int si, sj, ei, ej; int dir[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; int BFS() { while(!Q.empty()) Q.pop(); memset(vis, false, sizeof(vis)); vis[si][sj][0] = true; Q.push(node(si, sj, 0, L, 0)); while(!Q.empty()) { node cur = Q.top(); Q.pop(); //cout << cur.x << ' ' << cur.y << ' ' << cur.t << ' ' << cur.mob << ' ' << cur.state << endl; if(cur.x == ei && cur.y == ej) { if(cur.mob == L) return cur.t; return cur.t + 1; } for(int k = 0; k < 4; ++k) { int x = cur.x + dir[k][0], y = cur.y + dir[k][1]; if( x <= 0 || x > n || y <= 0 || y > m || g[x][y] == -1) continue; int mob = cur.mob - 1, state = cur.state; if(g[x][y] != 0) { int t = g[x][y]; if(!(cur.state & (1 << (t - 1))))//判断怪是否已经被杀死 mob = 0; } if(mark[x][y] >= 0) //怪所在的位置,则杀了怪,改变状态 state |= (1 << mark[x][y]); if(vis[x][y][state]) continue; int turn = cur.t; if(mob == 0) { mob = L; turn++; } vis[x][y][state] = true; Q.push(node(x, y, turn, mob, state)); } } return -1; } int main() { int x, y; while(scanf("%d %d %d",&n, &m, &L) == 3) { for(int i = 1; i <= n; ++i) for(int j = 1; j <= m; ++j) scanf("%d",&g[i][j]); memset(mark, -1, sizeof(mark)); int k; scanf("%d", &k); for(int i = 0; i < k; ++i) { scanf("%d %d",&x, &y); mark[x][y] = i; } scanf("%d %d %d %d",&si, &sj, &ei, &ej); int ans = BFS(); if(ans == -1) puts("We need God's help!"); else printf("%d\n", BFS()); } return 0; }