使用k币的路径数

时间:2016-05-18 22:30:40

标签: c++ algorithm dynamic-programming

  

给定一个矩阵,其中每个单元格都有一定数量的硬币。计算从左上角到右下方的确切k个硬币的数量。我们可以从单元格(i+1, j)移至(i, j+1)(i, j)

     

示例:

     

输入:k = 12

   mat[][] = { {1, 2, 3},
               {4, 6, 5},
               {3, 2, 1}
             };
     

输出:   2有两条路径,有12个硬币

     

1 - > 2 - > 6 - > 2 - > 1
  1 - > 2 - > 3 - > 5 - > 1

我为此创建了一个递归定义:

  

Count(i, j, k)成为使用M[0][0]硬币从M[i][j]升级到k的方式的数量。

Count(i, j, k) = {
   0:                                       if M[i][j] > k,
   Count(i-1, j, k-1) + Count(i, j-1, k-1): if M[i][j] < k
}

我对此定义的推理是,如果矩阵中的条目(硬币数量)大于我们想要的硬币数量(k),那么我们就不能采用这条路径了,所以表中的值应为0.

如果条目小于或等于硬币数量,那么我们可以通过添加顶部(i-1,j)和左(i, j-1)的路径数量来获取该路径。我将k减去1,因为最后一个条目的硬币数量减少了1个。

这就是我在以下动态编程功能中的做法:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

#define MAX_SIZE  10
#define MAX_COINS 20

int Count[MAX_SIZE][MAX_SIZE][MAX_COINS]; // number of ways to get from M[0][0] to M[i][j] using k coins
std::vector<std::vector<int>> M;

int NumOfPaths(int C) {
    size_t N = M.size();
    // Number of paths to (0,0) with 1 coin is 1
    Count[0][0][1] = 1;
    // zero coins doesn't work
    for (size_t i = 0; i < N; ++i) for (size_t j = 0; j < N; ++j)
        Count[i][j][0] = 0;

    // If the number of coins is greater than the max then Count[i][j][k] = 0;
    // Otherwise Count[i][j][k] = Count[i-1][j][k-1]+Count[i][j-1][k-1]

    for (size_t i = 1; i <= N; ++i) {
        for (size_t j = 1; j <= N; ++j) {
            for (int k = 1; k <= C; ++k) {
                if (M[i-1][j-1] >  k) Count[i][j][k] = 0;
                if (M[i-1][j-1] <= k) Count[i][j][k] = Count[i-1][j][k-1] + Count[i][j-1][k-1];
            }
        }
    }
    return Count[N][N][C];
}

int main() {
    M = { {1, 2, 3},
          {4, 6, 5},
          {3, 2, 1}
        };

    cout << NumOfPaths(12);
}

当我将函数应用于问题语句中给出的示例时,它返回0,这是不正确的。

我想知道我的推理出错的地方以及如何解决。

1 个答案:

答案 0 :(得分:2)

你的问题是

  • 您应该Count[0][0][1] = 1 Count[0][0][M[0][0]]初始化Count[0][j](虽然这里的内容相同)
  • 您永远不会填写Count[i][0]< N,也就是说,您从任何正在循环的单元格中找不到0,0的完整路径
  • 你和你的上层指数一个接一个;你希望循环中有M[i-1][j-1]并且当向量为零索引时返回N-1
  • 您正在循环中对M[i][j]的k M[i][j]进行测试而不是for (size_t i = 0; i < N; ++i) { for (size_t j = 0; j < N; ++j) { if ((i == 0) && (j == 0)) { // Skip 0,0: we've populated that already continue; } for (int k = 1; k <= C; ++k) { if (M[i][j] > k) Count[i][j][k] = 0; if (M[i][j] <= k) { int ways = 0; if (i >= 1) ways += Count[i - 1][j][k - M[i][j]]; if (j >= 1) ways += Count[i][j - 1][k - M[i][j]]; Count[i][j][k] = ways; } } } } return Count[N-1][N-1][C];
  • 你减去一枚硬币而不是onTimerComplete硬币(正如Edward和vu1p3n0x在评论中指出的那样)

这是一个固定循环:

MainTimer

另外,也许我误解了:你是不是故意将左上角的方格数计为1,1这样你就不需要检查我们是否为你进行了i-1和j-1检查,因为那里总会有一排零溢出来?这对于我认为的返回[N] [N]和M [i-1] [j-1]是有意义的。在那种情况下,你想要

  • 初始化1,1而不是0,0作为[1] [1] [1] = 1,并在循环中跳过1,1,如上所述
  • 现在从k减去M [i-1] [j-1]个硬币,而不是1
  • 如果您确实需要应对10x10,则将MAX_COINS增加1,否则您只能接受9x9