【问题标题】:Need help to optimize dynamic programming problem solution需要帮助优化动态规划问题的解决方案
【发布时间】:2021-01-23 06:36:54
【问题描述】:

问题陈述:

给定两个数组ab,大小分别为nm。这些数组中的所有数字都在 0 到 9 的范围内。让我们创建一个大小为n x m 的矩阵,其中i 行和j 列中的值等于ai * 10^9 + bj。找到从正方形1,1n,m 的最大和的路径。您可以向前或向下移动。

输入参数: 第一行包含nm (1

第二行包含数组a的值

第三行包含数组b的值

输出 打印最大和

时间限制:1 秒

内存限制:512MB

例子:

输入:

7 4
0 7 1 7 6 7 6
4 1 9 7

输出:55000000068

我尝试用动态编程来解决这个问题,但是我的解决方案在O(n * m) 有效并且无法通过时间限制:

#include <iostream>
#include <vector>
#include <cmath>

using namespace std;

int main() {
        int n, m; cin >> n >> m;

        vector<uint64_t> a(n), b(m);
        for(int i = 0; i < n; i++) {
            int tmp;
             cin >> tmp;
             a[i] = tmp * 10e8;
        }

        for(int i = 0; i < m; i++) cin >> b[i];

        vector<uint64_t> dp(m);
        dp[0] = a[0] + b[0];

        for (int i = 1; i < m; i++)
            dp[i] = dp[i-1] + a[0] + b[i];

        for (int i = 1; i < n; ++i) {
            for(int j = 0; j < m; ++j) {
                if (j == 0)
                    dp[j] = dp[j] + a[i] + b[j];
                else
                    dp[j] = max(dp[j], dp[j-1]) + a[i] + b[j];
            }
        }

        cout << dp.back() << endl;

        return 0;
}

【问题讨论】:

  • 我不确定,但是,使用带有记忆的递归函数可能会解决它。这是因为在递归函数中,您不会填充整个矩阵。
  • 如果您可以向前或向下移动,这是否意味着您需要两个索引来获得最佳解决方案?例如,正方形 (2,3) 可以从 (1,3) 或 (2,2) 到达
  • @HikmatFarhat someval 是什么意思?我在我的解决方案中使用了一维数组,因为使用二维数组我们不会超过内存限制
  • 请注意,这个问题不是对称的:通过停留在大数字的行上,您可以获得更多“点”。那么问题是“你什么时候从一大排跳到下一排”。
  • 旁白:int tmp; cin &gt;&gt; tmp; a[i] = tmp * 10e8; 可能会遇到精度问题,因为乘法不使用 64 位整数数学。建议 uint64_t tmp; OTOH,也许可以使用限制性 0-9 tmp 值。

标签: c++ algorithm dynamic-programming


【解决方案1】:

无需动态规划,并且需要 O(n+m) 内存和时间即可解决此问题。

正如 cmets 中的 @Botje 所指出的,a 中较高值的奖励非常大。因此,最优路径将保留在最左边的列中,直到它达到 a 中的最大值(在上面的示例中为 7)。如果这个最大值只在 a 中出现一次,那么最好的选择是消耗整个这一行,然后是所有后续行的最后一个元素,直到我们到达右下角。

但是,如果这个最大值出现不止一次,我们可以通过沿着最大值为 a 的第一行向右移动,直到我们到达最大值为的列b,然后将此列向下移动到包含 a 最大值的最后一行。然后我们可以像以前一样使用该行的其余部分,然后是所有后续行的最后一个元素。

也许插图会有所帮助:

    a = [ 0, 6, 9, 9, 0, 9, 3, 1 ]
    b = [ 1, 3, 2, 8, 4, 8, 1, 6 ]

   Col:  0     1     2     3     4     5     6     7
  Row:
   0    0,1   0,3   0,2   0,8   0,4   0,8   0,1   0,6 
         v
   1    6,1   6,3   6,2   6,8   6,4   6,8   6,1   6,6 
         v 
   2    9,1 > 9,3 > 9,2 > 9,8   9,4   9,8   9,1   9,6 
                           v
   3    9,1   9,3   9,2   9,8   9,4   9,8   9,1   9,6 
                           v
   4    0,1   0,3   0,2   0,8   0,4   0,8   0,1   0,6 
                           v
   5    9,1   9,3   9,2   9,8 > 9,4 > 9,8 > 9,1 > 9,6 
                                                   v
   6    3,1   3,3   3,2   3,8   3,4   3,8   3,1   3,6 
                                                   v
   7    1,1   1,3   1,2   1,8   1,4   1,8   1,1   1,6 

在这个例子中,有三行 a = 9,分别是第 2 行、第 3 行和第 5 行。要获得最高分,我们需要遵循这些行中的第一行(即 row 2)直到我们到达最大值为 b 的列(无论是第 3 列还是第 5 列,都没有区别)。然后向下移动到 a=9 的最后一行(第 5 行),向右移动到该行的末尾,最后向下移动到右下角。

我已将 Python 代码从该答案的早期版本转换为 C++。在数组 ab 中有 105 个随机值的测试中,它在我的系统上大约 0.3 秒产生结果。上面的动态规划解决方案给出了相同的结果,但需要大约 4 分钟:

#include <iostream>
#include <vector>

using namespace std;

int main() {
    int n, m;
    cin >> n >> m;

    vector<int64_t> a(n), b(m);
    int tmp, astart, aend, bmax, i, j;
    
    // Read in arrays a[] and b[]. At the same time,
    // find the first and last indices of the maximum
    // value in a[] (astart and aend) and any index
    // corresponding to the maximum value of b[] (bmax)
    
    for (tmp = -1, i = 0; i < n; i++) {
        cin >> a[i];
        if (a[i] >= tmp) {
            aend = i;
            if (a[i] > tmp) {
                astart = i;
                tmp = a[i];
            }
        }
        a[i] *= 1000000000LL;
    }
    for (tmp = -1, j = 0; j < m; j++) {
        cin >> b[j];
        if (b[j] > tmp) {
            tmp = b[j];
            bmax = j;
        }
    }
    
    // Trace through the matrix. First work down as far as
    // astart, then right until column bmax. Then work down
    // as far as row aend, add the remaining elements in this
    // row, and finally add the last element of each remaining
    // rows until we reach the bottom right corner.
    
    i = j = 0;
    int64_t tot = a[i] + b[j];
    while (i < astart) tot += a[++i] + b[j];
    while (j < bmax) tot += a[i] + b[++j];
    while (i < aend) tot += a[++i] + b[j];
    while (j < m-1) tot += a[i] + b[++j];
    while (i < n-1) tot += a[++i] + b[j];
        
    cout << tot << endl;
    return 0;
}

【讨论】:

    猜你喜欢
    • 2011-03-23
    • 2014-09-09
    • 1970-01-01
    • 2020-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多