从本文开始,我打算多刷一些动态规划的题。不仅如此,各种典型算法也会在分类刷一刷。
【题目】
给定一个矩阵,从左上角开始每次只能向右或者右下走,最后到达右下角的位置,路径上所有数字累加起来就是路径和,返回所有路径中最小的路径和。
【举例】
如果给定的m如下:
1,3,5,9
8,1,3,4
5,0,6,1
8,8,4,0
路径1,3,1,0,6,1,0是所有路径中路径和最小的,所以返回12。
【解答】
这道题是很明显的动态规划算法,动态规划算法的一般步骤就是列一张表来存放计算过的值,通常采用数组实现。这道题的特殊之处在于表的值不仅和自身有关,而且和原矩阵也是相关的。表中的值需要和原矩阵中的值相加,因为我们求的是路径和。
矩阵如下,只画了一部分,动态规划通常会申请+1的空间,i=0和j=0用来存放动态规划的初始值,这里我们赋为0,这张表是B[m+1][n+1]。
:0 1 2 3 4
0: 0 0 0 0 0
1: 0 1 4 9 18
2: 0 9 5
...
不难看出,B[1][j]和B[i][1]是取它上下两个相邻值再加上原数组A[i-1][j-1]的值(看1和4),为什么减一是因为规划表真实数据从1开始而不是从0开始。这些其实是为了初始化下标为1的这些点。
除了下标为i=1或j=1的部分,其他值(看5)是上下相邻两个值得最小值与原数组A[i-1][j-1]的值相加。这就是dp数组的公式了。
我第一种解法就没有初始化,直接放在循环里面做了,不过效率肯定不高,下面是我第一种解法,可以和第二种对比一下。
for(int i=1; i<=m; ++i){
for(int j=1; j<=n; ++j){
if(i == 1 || j == 1)
B[i][j] = std::max(B[i-1][j], B[i][j-1]) + A[i-1][j-1];
else
B[i][j] = std::min(B[i-1][j], B[i][j-1]) + A[i-1][j-1];
}
}
其实最早的第一次解我都没有考虑到下标为1的应该是max,当时我直接只用了一个min,然后求得结果为11,还好我认识到了错误。
起始也不用std::max,直接相加就行了
下面看第二种完整代码,注意第二种只是参考,还有第三种,我没有删除这个是为了警示我自己考虑要周全,突然又想到的。
int min_path(const std::vector<std::vector<int>>& A)
{
if(A.size() == 0)
return 0;
int m = (int)A.size();
int n = (int)A[0].size();
std::vector<std::vector<int>> B(m+1, std::vector<int>(n+1, 0));
for(int i=1; i<=m; ++i)
B[i][1] = B[i-1][1] + A[i-1][0];
for(int j=1; j<=n; ++j)
B[1][j] = B[1][j-1] + A[0][j-1];
for(int i=2; i<=m; ++i){
for(int j=2; j<=n; ++j){
B[i][j] = std::min(B[i-1][j], B[i][j-1]) + A[i-1][j-1];
}
}
return B[m][n];
}
第三种:分配同样的大小就可以了,不需要i-1和j-1了。
int min_path(const std::vector<std::vector<int>>& A)
{
if(A.size() == 0)
return 0;
int m = (int)A.size();
int n = (int)A[0].size();
std::vector<std::vector<int>> B(m, std::vector<int>(n, 0));
B[0][0] = 1;
for(int i=1; i<m; ++i)
B[i][0] = B[i-1][0] + A[i][0];
for(int j=1; j<n; ++j)
B[0][j] = B[0][j-1] + A[0][j];
for(int i=1; i<m; ++i){
for(int j=1; j<n; ++j){
B[i][j] = std::min(B[i-1][j], B[i][j-1]) + A[i][j];
}
}
return B[m-1][n-1];
}
第三种直接分配相同大小的空间就可以了。唉,动态规划分配空间就是要么加1,要么不加1,我还是不够熟练。
测试用例:
int main()
{
std::vector<std::vector<int>> A = { {1, 3, 5, 9},
{8, 1, 3, 4},
{5, 0, 6, 1},
{8, 8, 4, 0}};
int find = min_path(A);
std::cout<<"the min path is : "<<find<<std::endl;
typedef std::vector<std::vector<int>>::iterator iterator1;
typedef std::vector<int>::iterator iterator2;
for(iterator1 iter= A.begin(); iter!=A.end(); ++iter){
for(iterator2 it = (*iter).begin(); it!=(*iter).end(); ++it)
std::cout<<*it<<' ';
std::cout<<std::endl;
}
return 0;
}
继续刷题中。。。