13
我的思路:
仿照hdoj1044的DFS+BFS思路(很是相似, 只是时间重置不太一样)
因为路可以来回走, 所以将“4”作为特殊点对待, 将每一个特殊点(起点、终点、和每一个“4”)都作为起点遍历一次图,并且将他们编号, 通过多次遍历就可以得到这些点之间的距离(遍历过程中如果发现两个特殊点之间的距离大于或者等于6就跳过【这里就是这题的不同点】,距离也就默认为初始化时的INF),然后用DFS来判断从起点出发, 走哪几个“4”特殊点(或者不走“4”直接从起点到达终点)到达终点所用的时间最短, 或者无论如何也走不到终点!!
AC代码:(估计数据很弱,运气好,0ms过了, 最坏情况要遍历将近64张图, 然后还要用DFS来搜索64个点的走法(这个耗时很凶, 这个时候就炸了, 这种方法对这题显然是个野路子, 只能小数据的时候使用))。
#include
#include
#include
#include
#include
using namespace std;
struct node{
int x, y;
int t;
}s, u, v;
const int INF = 1e8;
const int maxn = 9;
int G[maxn][maxn];
int path[maxn*maxn][maxn*maxn];
int dir[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
int N, M, L, var, ans;
bool vis[maxn][maxn], vis2[maxn*maxn];
void bfs(int x, int y, int from) {
memset(vis, false, sizeof(vis));
s.x = x; s.y = y; s.t = 0;
vis[s.x][s.y] = true;
queue que;
que.push(s); //这个居然也会漏掉。。
while(!que.empty()) {
u = que.front(); que.pop();
//cout << u.x << " " << u.y << endl;
for(int i = 0; i < 4; i++) {
v.x = u.x + dir[i][0];
v.y = u.y + dir[i][1];
if(v.x < 0 || v.y < 0 || v.x >= N || v.y >= M || G[v.x][v.y] == 0 || vis[v.x][v.y]) continue;
vis[v.x][v.y] = true;//访问标记 常常忘记 这里漏掉会导致无限死循环
v.t = u.t+1;
if(G[v.x][v.y] != 1 && v.t < 6){
//if(v.x == 3 && v.y == 5) cout << "终点" << u.x <<" "<< u.y << "|" << v.x <<" "<< v.y << " " << v.t << endl;
if(G[v.x][v.y] == 2) path[from][0] = v.t;
else if(G[v.x][v.y] == 3) path[from][L+1] = v.t;
else path[from][G[v.x][v.y] - 53] = v.t;
//v.t = 0; 删掉了这一行 然后对了 显然我对这个过程分析的还不是很透彻
}
que.push(v);
}
}
}
void dfs(int pos, int steps) {
if(steps >= ans) return ;
if(pos == L+1) {
ans = min(ans, steps);
// cout << "结束: ";
// for(int i = 1; i <= L+1; i++) {
// if(vis2[i] == true) cout << "走 " << i ;
// }
// cout << " 总路程是: " << ans << endl;
return ;
}
for(int i = 1; i <= L+1; i++) {
if(vis2[i]) continue;
vis2[i] = true;
//cout << "走" << i << "路程" << path[pos][i] << "当前总路:" << steps + path[pos][i] << endl;
dfs(i, steps + path[pos][i]);
vis2[i] = false;
}
}
int main() {
int T; scanf("%d", &T);
while(T--) {
L = 0; var = 49; ans = INF;
scanf("%d%d", &N, &M);
for(int i = 0; i < N; i++)
for(int j = 0; j < M; j++) {
scanf("%d", &G[i][j]);
if(G[i][j] == 4) {
G[i][j] += (++var);
L++;
// cout << G[i][j] << endl;
}
}
for(int i = 0; i <= L+1; i++) {
for(int j = 0; j <= L+1; j++) {
path[i][j] = INF;
}
}
// for(int i = 0; i < N; i++) {
// for(int j = 0; j < M ; j++)
// if(G[i][j] == 2) cout << "S ";
// else if(G[i][j] == 3) cout << "E ";
// else if(G[i][j] == 0) cout << "| ";
// else if(G[i][j] > 50 ) cout << G[i][j] - 53<< " ";
// else cout << " ";
// cout << endl;
// }
for(int i = 0; i < N; i++)
for(int j = 0; j < M; j++) {
if(G[i][j] > 50) bfs(i, j, G[i][j]-53);
else if(G[i][j] == 3) bfs(i, j, L+1);
else if(G[i][j] == 2) bfs(i, j, 0);
}
// cout << path[1][4] << endl;
// cout << path[2][4] << endl;
// cout << path[3][4] << endl;
memset(vis2, false, sizeof(vis2));
dfs(0, 0);
// cout << ans << endl;
if(ans == INF) printf("-1\n");
else printf("%d\n", ans);
}
return 0;
}
现在来看看第二种做法:单纯的BFS解可往返点(这是我第一次只用一个BFS解答可以来回走的路的题目,参考了一些大神博客的题解)
这里要注意的是, 所有的“4”都只可以走一次, 重复走得话就没有意义, 可以想象一个背着炸弹的“炸弹人”刚跑到“4”重置了炸弹时间, 然后他出去走了两步, 然后又走回来两步重置炸弹的话就是在死循环, 其他的点(2【起点】, 3【终点】, 1【通道】)都可以来回走, 原因是起点和通道以及终点都是一样的, 其实都可以看成通道, 都可以来回走, 唯一不同的就是到了目标点(终点)这个点的时候, 统计一下最少步数即可。
AC代码:(比较上面的AC代码, 这个非常可靠(还是单纯的BFS!!))
#include
#include
#include
#include
#include
using namespace std;
struct node {
int x, y, t, steps;
}s, u, v;
const int INF = 1e8;
const int maxn = 9;
const int dir[4][2] = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
bool vis[maxn][maxn];
int G[maxn][maxn];
int N, M;
int ans;
void bfs() {
memset(vis, false, sizeof(vis));
//vis[s.x][s.y] = true;
queue que;
que.push(s);
while(!que.empty()) {
u = que.front(); que.pop();
for(int i = 0; i < 4; i++) {
v.x = u.x + dir[i][0];
v.y = u.y + dir[i][1];
v.t = u.t-1;
v.steps = u.steps+1;
if(v.x < 0 || v.y < 0 || v.x >= N || v.y >= M || G[v.x][v.y] == 0 || vis[v.x][v.y] || v.t <= 0 || v.steps >= ans) continue;
if(G[v.x][v.y] == 4) {
v.t = 6;
vis[v.x][v.y] = true;
}
else if(G[v.x][v.y] == 3) {
ans = min(ans, v.steps);
}
que.push(v);
}
}
}
int main() {
int T; scanf("%d", &T);
while(T--) {
scanf("%d%d", &N, &M);
for(int i = 0; i < N; i++)
for(int j = 0; j < M; j++) {
scanf("%d", &G[i][j]);
if(G[i][j] == 2) {
s.x = i; s.y = j;
s.t = 6; s.steps = 0;
}
}
ans = INF;
bfs();
if(ans == INF) printf("-1\n");
else printf("%d\n", ans);
}
return 0;
}