洛谷p2802 回家(dfs回溯)

题目描述
小H在一个划分成了n*m个方格的长方形封锁线上。 每次他能向上下左右四个方向移动一格(当然小H不可以静止不动), 但不能离开封锁线,否则就被打死了。 刚开始时他有满血6点,每移动一格他要消耗1点血量。一旦小H的 血量降到 0, 他将死去。 他可以沿路通过拾取鼠标(什么鬼。。。)来补满血量。只要他走到有鼠标的格子,他不需要任何时间即可拾取。格子上的鼠标可以瞬间补满,所以每次经过这个格子都有鼠标。就算到了某个有鼠标的格子才死去, 他也不能通过拾取鼠标补满 HP。 即使在家门口死去, 他也不能算完成任务回到家中。

地图上有 5 种格子:

数字 0: 障碍物。

数字 1: 空地, 小H可以自由行走。

数字 2: 小H出发点, 也是一片空地。

数字 3: 小H的家。

数字 4: 有鼠标在上面的空地。

小H能否安全回家?如果能, 最短需要多长时间呢?

输入格式
第一行两个整数n,m, 表示地图的大小为n*m。

下面 n 行, 每行 m 个数字来描述地图。

输出格式
一行, 若小H不能回家, 输出-1,否则输出他回家所需最短时间。

输入输出样例
输入 #1复制
3 3
2 1 1
1 1 0
1 1 3
输出 #1复制
4
说明/提示
1<=n,m<=9
题目分析:
开始用bfs得了80分发现了这个数据
8 4
3 1 1 1
1 1 1 0
1 1 1 1
0 1 1 2
1 4 4 1
1 4 0 1
1 0 1 1
1 0 1 1
仔细一想确实不行,因为bfs不会自己选择既能到达又能不死的路径,所以就需要优化一下,但没想通啊!
后来想到可以用dfs再回溯一下暴力的搜索每到达一个点四个方向的走法(注意不回溯的话要么堵死要么没血)。但写了之后得了90分,又发现了一个数据
7 6
2 0 0 0 0 0
1 0 0 0 0 0
1 1 4 0 0 0
1 0 0 0 0 0
1 1 1 1 1 3
4 0 1 0 4 0
0 0 4 0 0 0
天呐,这题坑也太多了,一个格子可以走多次,并不是平常的走过的格子就不能走了,所以这里不能用一个二维数组表示一个格子走没走过!而是要回溯步数!!这样就可以使一个格子走多次了。
但是,这样回溯的话不用计算机算就知道要选择的路径是非常多的,大数据必定TLE,所以还是要优化一下。
首先,地图就那么大,如果步数超过了地图行列乘积的大小,那么自然是不行的。所以要保证步数sum<=m*n。
其次,如果你已经到达过一次终点了,那么最小步数也已经更新过了,那么你再选择另外一条路走并且当前步数大于已经更新过的最小步数也是不行的,也就是当前步数sum<=ans(最后要输出的结果),否则就返回上一层。
代码:

#include
using namespace std;
int n,m,ex,ey;
int tu[101][101];
int d[4][2]={1,0,-1,0,0,1,0,-1};
int ans=9999,sum=0;
void dfs(int,int,int);
int main()
{
    int bx,by;
    cin>>n>>m;
    for(int i=0;i<=n+1;i++)
        for(int j=0;j<=m+1;j++)
    {
        if(i==0||i==n+1||j==0||j==m+1)//直接对边界进行标记,全部赋值为零
            tu[i][j]=0;
        else cin>>tu[i][j];
        if(tu[i][j]==2) {bx=i;by=j;tu[i][j]=1;}
        if(tu[i][j]==3) {ex=i,ey=j;tu[i][j]=1;}
    }
    tu[bx][by]=0;
    dfs(bx,by,6);
    if(ans==9999) cout<<-1;//如果结果没有更改,那么肯定到达不了
    else cout<<ans;//结果更改了就代表到达了
    return 0;
}
void dfs(int x,int y,int hp)
{
    if(hp==0||sum>m*n||sum>ans) return;//没血了或者步数越界了或者当前步数比当前已知的最小步数大了就返回上一层
    if(x==ex&&y==ey)//到达了家
    {
        ans=min(sum,ans);//选择最优解
        return;
    }
    if(tu[x][y]==4) hp=6;
    for(int i=0;i<4;i++)
    {
        int xx=x+d[i][0];
        int yy=y+d[i][1];
        if(tu[xx][yy]!=0)
        {
            sum++;//步数加一
            dfs(xx,yy,hp-1);
            sum--;//回溯
        }
    }
}

结果:

输入:
3 3
2 1 1
1 1 0
1 1 3
输出:
4
Process returned 0 (0x0)   execution time : 14.257 s
Press any key to continue.

你可能感兴趣的:(搜索与回溯)