使用动态编程从数组的左上角到右下角元素

时间:2016-12-17 10:42:51

标签: c++ dynamic-programming

我很难想出一种为动态编程问题制定解决方案的方法。基本上,我必须从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;
}

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

1 个答案:

答案 0 :(得分:2)

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

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

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

例如,拿这个董事会:

enter image description here

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

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

基于以上所述,您可以使用地图,因为您正在使用c ++:

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

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

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 ++中有点生疏,但希望你有这个想法: 在实际计算解决方案之前,请检查该对的映射。如果该对存在,那么您已经解决了问题的这一部分,所以从地图中获取解决方案并避免递归调用。