动态规划:计算连续跳跃列表的所有可能结束位置

Posted

技术标签:

【中文标题】动态规划:计算连续跳跃列表的所有可能结束位置【英文标题】:Dynamic Programming: Calculate all possible end positions for a list of consecutive jumps 【发布时间】:2014-11-17 10:42:40 【问题描述】:

问题在于计算所有可能的结束位置以及每个位置存在多少组合。

给定起始位置x=0、轨道长度m 和跳跃列表。返回区间 [-m/2,+m/2] 上每个位置的可能结束数。跳跃必须按照给定的顺序进行,但可以以消极或积极的方式完成。

例如:

L = 40
jumps = 10, 10

解决方案:

-20 : 1  (-10, -10)
0 : 2 (-10,+10 & +10,-10)
20 : 1 (+10,+10)

(需要的输出只有一对“位置:#combinations”)

我做了一个简单的递归,结果还可以。 但在大量数据中,执行时间是几分钟或几小时。 我知道使用动态编程可以在几秒钟内得到解决方案,但我不知道在这种情况下如何应用动态。

这是我实际的递归函数:

void escriuPosibilitats(queue<int> q, map<int,int> &res, int posicio, int m) 
    int salt = q.front();
    q.pop();
    if(esSaltValid(m,posicio,-salt)) 
        int novaPosicio = posicio - salt;
        if(q.empty()) 
            res[novaPosicio]++;
         else 
            escriuPosibilitats(q,res,novaPosicio,m);
        
    
    if(esSaltValid(m,posicio,salt)) 
        int novaPosicio = posicio + salt;
        if(q.empty()) 
            res[novaPosicio]++;
         else 
            escriuPosibilitats(q,res,novaPosicio,m);
        
    

    其中q 是剩余跳转的队列。 res 是主要解决方案。 posicio 是实际位置。 m 是轨道的长度。 其中esSaltValid 是一个检查跳转是否在轨道长度范围内有效的函数。

PD:对不起我的英语水平。我试图改善我的问题!谢谢=)

【问题讨论】:

【参考方案1】:

您可以使用以下想法。令 dp[x][i] 为在跳转 i 之前到达位置 x 的路径数。那么答案将是每个 x 的 dp[x][N],其中 N 是跳跃次数。更重要的是,你可以意识到这个 dp 只依赖于前一行,然后你可以简单地 dp[x] 并将下一行保存在某个辅助数组中,然后在每次迭代中替换它。代码是这样的:

const int MOD = (int)(1e8+7);
    const int L = 100;
    int N = 36;
    int dx[] = 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1;

    int dp[L+1];
    int next[L+1];

    int main() 
      int shift = L/2; // to handle negative indexes
      dp[shift] = 1; // the initial position has one way to arrive, since you start there
      for (int i = 0; i < N; ++i)  // for each jump size
        for (int x = -L/2; x <= L/2; ++x)  // for each possible position
          if (-L/2 <= x + dx[i] && x + dx[i] <= L/2) // positive jump
            next[x + shift] = (next[x + shift] + dp[x + dx[i] + shift]) % MOD;
          if (-L/2 <= x - dx[i] && x - dx[i] <= L/2) // negative jump
            next[x + shift] = (next[x + shift] + dp[x - dx[i] + shift]) % MOD;
        
        for (int x = -L/2; x <= L/2; ++x)  // update current dp to next and clear next
          dp[x+shift] = next[x+shift];
          next[x+shift] = 0;
        
      
      for (int x = -L/2; x <= L/2; ++x) // print the result
        if (dp[x+shift] != 0) 
          cout << x << ": " << dp[x+shift] << '\n';  
        
    

当然,如果 L 太大而无法处理,您可以压缩状态空间并将结果保存在映射中,而不是保存在数组中。该方法的复杂度为 O(L*N)。希望对您有所帮助。

编辑:只需计算所有模 1e8+7 即可。

【讨论】:

在某些情况下是不正确的。例如:L = 100 N = 36 dx = 1s 例如在:L = 100 N = 36 dx = 1s 它返回负数和错误数字 假设“1s”在这种情况下表示 dx 是一个由 36 个 1 组成的数组,那么负数是因为结果太大而无法容纳 32 位整数。将类型更改为 long long。 @user1782922 究竟哪个值是错误的?他们在我看来还不错。 例如:-16返回254186856,正确答案是54186842

以上是关于动态规划:计算连续跳跃列表的所有可能结束位置的主要内容,如果未能解决你的问题,请参考以下文章

js-leetcode前端动态规划入门第四天刷题打卡「跳跃游戏」贪心大法!

计蒜客 跳跃游戏二(动态规划)

LeetCodeLeetCode之跳跃游戏Ⅱ——暴力解法+动态规划+贪婪算法

LeetCodeLeetCode之跳跃游戏Ⅱ——暴力解法+动态规划+贪婪算法

数据结构与算法|讲解|动态规划一脸懵?看完之后轻松掌握!

#yyds干货盘点# 动态规划专题:跳跃游戏