动态编程:计算连续跳转列表的所有可能的结束位置

时间:2014-11-17 10:42:40

标签: c++ algorithm dynamic-programming

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

给定起始位置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)

(所需的输出只是“position:#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);
        }
    }
}
  1. 其中q是剩余跳跃的队列。
  2. 其中res是个别解决方案。
  3. 其中posicio是实际位置。
  4. 其中m是曲目的长度。
  5. 其中esSaltValid是一个检查跳转在轨道长度范围内是否有效的函数。
  6. PD:对不起我的英语水平。我试着改进我的问题!谢谢=)

1 个答案:

答案 0 :(得分: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的所有内容,就是这样。