过去会遗忘,现在不会

机器人走路问题

就是一个坐标轴,从1开始到N,机器人处于其中某个位置cur,问它走到另一个位置aim有几种走法?限定机器人必须走k步。且在1或N位置的时候,只能返回2或N-1,其余位置两个方向都能走。

 

首先理解一下递归。

机器人从cur位置开始走,假如这个cur在1这个位置,那么它只能去2;假如cur在N这个位置,它只能去N-1;假如cur在中间,那么cur可以去cur-1或者cur+1的位置。

此时机器人只走了1步,剩下k-1步。

那么下一步机器人处在一个新位置,这个位置我们从上一步结果可以知道,因此递归。

递归出口就是k=0,表示机器人走完。走完就代表一个机器人的一个走法。

判断这个走法是不是我们要的结果,就是判断cur和aim有没有重合?重合代表走对了,返回1,否则,这个走法不是我们要的,返回0;

int way1(int cur, int aim, int k, int N)
{
    if (k == 0) return cur == aim ? 1 : 0;

    if (cur == 1) return way1(2, aim, k - 1, N);
    if (cur == N) return way1(N - 1, aim, k - 1, N);


    return way1(cur - 1, aim, k - 1, N) + way1(cur + 1, aim, k - 1, N);
}

接下来我们要优化这个递归过程。为什么?因为递归的过程有大量的重复计算。

比方说机器人已经开始走了,它走到了5这个位置,那么5可以从4过来,也可以从6过来。

想一下,假设我们规定机器人从1开始走100步,坐标轴长度N=10。

机器人出现在5和6这个位置很多次,其中一定有存在,机器人走了同样的步数,走到了5或者6。

这在我们的递归里,就是重复计算的过程。

所以加上一个缓存表,存的是,机器人在当前位置和剩余步数的情况下,递归是否计算过这个数据。

假如计算过,直接返回计算的值;没计算过,更新缓存表。

//二维缓存表cs,维度<N+1,k+1>,初始化-1,表示没计算过。

int way2(int cur, int aim, int k, int N,vector<vector<int>>& cs)
{
    if (cs[cur][k] != -1) return cs[cur][k];
    int ans = 0;
    if (k == 0) ans = (cur == aim ? 1 : 0);
    else if (cur == 1) ans = way2(2, aim, k - 1, N, cs);
    else if (cur == N) ans = way2(N - 1, aim, k - 1, N, cs);
    else ans = way2(cur - 1, aim, k - 1, N, cs) + way2(cur + 1, aim, k - 1, N, cs);
    cs[cur][k] = ans;

    return ans;
}

继续优化就有点蒙了,是根据表找依赖关系,直接得到答案。

(这也是动态规划难的地方,就像数学公式,不给你推导,只告诉你结论,谁知道怎么来的?

所以最后还是从浅入深才能领悟精髓啊。)

完全理解之后再补充。

 

posted on 2023-06-22 03:44  WhatAnyWay  阅读(49)  评论(0)    收藏  举报