题解 P2802 【回家】

· · 题解

题目传送门 P2802

首先最短路一开始想到的是dijkstra或者bfs。

而这题由于会回满血的缘故,dijkstra较为难描述

当然,咱都是搜索dfs进来的

分析这道题可注意到以下几点:

比如这组数据:

0 0 0 4 0 0 0

0 0 0 1 0 0 0

3 1 1 ① 1 1 2

注意打圈的①那个点,我虽然直线从2走到3正好是没血的,不能到家,但是我可以走到①然后往上走,回满血,之后再从①出口,往家走,这样步数为10。然而我们发现,我们不能标记每一格只走一次,所以在这里,我们的vis应该是步数限制而不是 走过了就不走!我试了一下,最多可以设置到,每个格子至多走22次,多了会超时或栈溢出MLE

接下来就在代码里了~

#include<bits/stdc++.h>
#define MAXN 10
#define inf 0x3f3f3f3f
using namespace std;
int a[MAXN][MAXN], vis[MAXN][MAXN];
int n, m, sx, sy, fx, fy;// (sx,sy)为起点坐标,(fx,fy)为终点坐标,当然n,m是行和列
int sum;//用来统计最少步数的,一开始要初始化为最大(如上面预处理inf)
int to[][2] = { {0,1},{0,-1},{-1,0},{1,0} };//模拟4个方向
void dfs(int u, int v,int ans,int t)//(u,v)代表当前小H所在坐标,ans记录当前的血量,t记录到目前为止已耗费的时间
{
    if (u == fx && v == fy)//到家啦~
    {
        sum = min(sum, t);//保留最小步数
        return;
    }
    if (t >= sum)//这个if是后加的,是为了剪枝(之前没加也过了。。)当然,如果都模拟到这一步了,小H还没到达终点时,t就已经比之前的一种到达家的路径所耗费时间更多了,这条路就不再下去了
        return;
    if (ans > 1)//这是之前所说的,我在模拟每一步之前,我的血量一定要大于1,不然就返回了(返回相当于这段的dfs没执行了,因为if外边下面啥没有= =)
    {
        for (int i = 0; i < 4; i++)//模拟出上下左右走
        {
            int q = u + to[i][0], w = v + to[i][1];//模拟走一格之后的坐标
            if (q >= 0 && q < n&&w >= 0 && w < m && vis[q][w]<=22 && a[q][w] != 0)//走到这个格子时,我不能超边界&&限制每个格子所走的步数&&不能是大板墙~
            {
                vis[q][w] ++;//走过的次数+1
                if (a[q][w] == 4)
                {
                    dfs(q, w, 6, t + 1);//如果我走到了4了,当然我就回满血了~~ 并且步数+1
                }
                else
                {
                    dfs(q, w, ans - 1, t + 1);//否则我走到这只是扣血,步数也要+1
                }
                vis[q][w] --;//减少这个格子的次数,消除影响
            }
        }
    }
}
int main()
{
    cin >> n >> m;
    sum = inf;//初始化
    memset(vis, 0, sizeof(vis));//初始化
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            cin >> a[i][j];
            if (a[i][j] == 2)
                sx = i, sy = j;//找出起点坐标
            else if (a[i][j] == 3)
                fx = i, fy = j;//找出家坐标
        }
    }
    dfs(sx, sy, 6, 0);//从起点开始,血量为6,步数为0。开始深搜
    if (sum == inf)//如果sum没有被更新,那就说明没有达到家
        cout << "-1" << endl;
    else
        cout << sum << endl;//否则输出这个最短时间
}

祝各位以及忙碌的审核人新年快乐~