【问题标题】:Getting from top left to bottom right element of array using dynamic programming使用动态编程从数组的左上角到右下角元素
【发布时间】:2017-05-03 00:53:30
【问题描述】:

我很难想出一种方法来解决动态编程问题。基本上,我必须从 NxN 数组中的左上角到右下角的元素,只能向下或向右移动,但我应该移动到更大的元素并将其汇总到一个变量中(通过仅向右移动获得最高分和向下)。 F.e.,如果我有这个矩阵:

0 1 1

0 4 2

1 1 1

它应该移动 0-> 1 -> 4 -> 2 -> 1 并打印出 8。 我已经阅读了很长时间的动态优化,但仍然无法解决这个问题。如果有人可以帮助我,将不胜感激。 提前致谢!

编辑:谢谢@sestus!我已经设法解决了这个问题,但是解决方案很慢,我必须对其进行优化以更快地执行。这是我的解决方案:

#include <iostream>
#include <algorithm>
using namespace std;
const int MAX = 100;
int arr[MAX][MAX];

int move(int row,int col, int n)
{
if(row >= n || col >= n)
{
    return 0;
}
return max(arr[row][col] + move(row + 1, col, n),
           arr[row][col] + move(row, col + 1, n));
}

int main()
{
int examples, result;
cin>>examples;
int n;
int results[examples];
for(int k =1; k <= examples; k++)
{
    cin >> n;
    int s = 0;
    int i = 0, j = 0;
    for(i = 0; i < n; i++)
    {
        for(j = 0; j < n; j++)
        {
            cin>> arr[i][j];
        }
    }
    i = 0, j = 0;
    s+=move(i,j, n);
    results[k] = s;
}
for(int k = 1; k <= examples; k++)
{
    cout<<results[k]<<endl;
}
return 0;
}

(程序实际上必须获取所有示例并在最后输出所有示例的答案)。介意帮我优化吗?

【问题讨论】:

    标签: c++ dynamic-programming


    【解决方案1】:

    我不会在这里粘贴现成的代码,我只是给你一个解决方案的高级描述。

    那么在决定搬到哪里时,您有哪些可能的选择?您可以向下或向右移动,添加当前块的值。您的目标是最大化您访问过的区块的总和,直到您到达右下角的区块。这给了我们:

    move(row, column):
       //handle the cases when you move out of bounds of the array here
       return(max{array[row,column] + move(row + 1, column),
                  array[row,column] + move(row, column + 1)})
    

    要使上述内容成为完整的动态规划解决方案,您需要添加一些记忆,例如获取您已经解决的问题的值,而无需再次计算它们。在 stackoverflow 上查看更多详细信息:Dynamic programming and memoization: bottom-up vs top-down approaches

    以这个板子为例:

    注意两条不同的路线。我们已经通过两条不同的路线到达block[1][2](红线和蓝线结束的值为3的那个)。根据蓝色路线,我们向右下方移动,而我们通过读取向右下方移动。我粘贴的伪代码表明我们将首先采用蓝色路线,因为我们在递归调用 move(row, column + 1) 之前遇到了递归调用 move(row + 1, column)

    所以,当我们从红色路线到达block[1][2] 时,我们实际上不需要再次计算这个解。当我们通过读取路线回到那里时,我们已经这样做了!如果我们将这个解决方案保存在一个数组(或映射/哈希表)中,我们就可以选择解决方案而无需再次计算它。那就是记忆!

    基于上述,您可以使用地图,因为您使用的是 c++:

    std::map<std::pair<int, int>, int> cache;
    

    在进行递归调用之前,您需要检查该对是否存在于地图中。如果没有,您将其添加到地图中。所以move变成了:

    int move(int row,int col, int n)
    {
        if(row >= n || col >= n)
        {
            return 0;
        }
        pair<int, int> rowplus_column = make_pair(row + 1,col);
        pair<int, int> row_columnplus = make_pair(row, col + 1);
        int solution_right = 0;
        int solution_down = 0;
        map<char, int>::iterator it;
    
        it = cache.find(rowplus_column);
        if (it == cache.end())  {
           solution_down = move(row + 1, col);
           cache.insert(rowplus_column, solution_down);
        }
        else {
            solution_down = it->second;
        }
        it = cache.find(row_columnplus);
        if (it == cache.end())  {
           solution_right = move(row, col + 1);
           cache.insert(row_columnplus, solution_right);
        }
        else {
           solution_right = it->second;
        }
        return max(arr[row][col] + solution_down,
               arr[row][col] + solution_right);
    }
    

    我对 C++ 有点生疏,但希望你明白: 在实际计算解决方案之前,请检查该对的映射。如果该对存在,则您已经解决了该部分问题,因此请从地图中获取解决方案并避免递归调用。

    【讨论】:

    • 是的,谢谢你的回答,我能理解你的意思,但很遗憾我无法实现它:(
    • 我希望现在更清楚了!修复任何语法错误(自从我上次编写 C++ 以来已经有一段时间了),并使这个地图“缓存”全局(不要在 move 函数中定义一个新的)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-12-21
    • 2019-03-06
    • 2017-09-19
    • 2021-12-21
    • 2013-12-22
    • 2019-07-10
    • 1970-01-01
    相关资源
    最近更新 更多