【动态规划专题】2:矩阵的最小路径和
《程序员代码面试指南--IT名企算法与数据结构题目最优解》 左程云 著
矩阵的最小路径和
【题目】
给定一个矩阵m,从左上角开始每次只能向右或者向下走,最后到达右下角位置,路径上所有的数字累加起来就是路径和,返回所有路径中最小的路径和。
【举例】
如果给定的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.
关键思路:
假设有一个M*N的数组 dp[M][N], dp[i][j]的值表示从左上角(0,0)位置走到(i,j)位置的最小路径和。
第0行和第0列比较特殊,是不断累加的结果
1 4 9 18
9,
14
22
其他行和列,dp[i][j] = Min(dp[i-1][j], dp[i][j-1]) + m[i][j];
所以,我们可以从最右下角,倒推到左上角。
从下到上思考,从上到下解决。
#include <iostream> #include <stack> #include <exception> using namespace std; int minOfTwoNum(int A, int B) { return A > B ? B : A; } int MaxOfTwoNum(int A, int B) { return A > B ? A : B; }
//申请一个M*N的二位数组空间 int MinPathRoad(int* arrPath, const int rows,const int cols) { if (arrPath == nullptr || rows < 0 || cols < 0) { throw new std::exception("Invalid para"); } if (rows == 0 || cols == 0) { return 0; } int result = 0; int** copyPath =new int*[rows]; for (int i = 0; i < rows; i++) { copyPath[i] = new int[cols]; } ///这里有可优化空间。能用一维数组解决的,不要用二维数组 //for (int i = 0; i < rows; i++) //{ // for (int j = 0; j < cols; j++) // { // if (i == 0 && j == 0) // { // copyPath[i][j] = *(arrPath + i*cols + j); // } // if (i == 0 && j>0) // { // copyPath[i][j] = *(arrPath + i*cols + j) + *(arrPath + i*cols + j - 1); // } // if (j == 0 && i > 0) // { // copyPath[i][j] = *(arrPath + i*cols + j) + *(arrPath + (i - 1)*cols + j); // } // } //} copyPath[0][0] = *(arrPath); for (int j = 1; j < cols; j++) { copyPath[0][j] = *(arrPath + j) + copyPath[0][j-1]; ///这里容易出错 } for (int i = 1; i < rows; i++) { copyPath[i][0] = *(arrPath + i*cols) + copyPath[i - 1][0]; ///这里容易出错 } ////时间复杂度是O(M*N) for (int i = 1; i < rows; i++) { for (int j = 1; j < cols; j++) { int tempValue = minOfTwoNum(copyPath[i - 1][j], copyPath[i][j - 1]); copyPath[i][j] = minOfTwoNum(copyPath[i - 1][j], copyPath[i][j - 1]) + *(arrPath + i*cols + j); } } result = copyPath[rows - 1][cols - 1]; for (int i = 0; i < rows; i++) { delete[] copyPath[i]; } delete[] copyPath; return result; }
//申请一个一维数组空间 int MinPathRoad2(int* arrPath, const int rows, const int cols) { if (arrPath == nullptr || rows < 0 || cols < 0) { throw new std::exception("Invalid para"); } if (rows == 0 || cols == 0) { return 0; } int result = 0; //用一个一维数组来存储临时变量 int* copyPath = new int[cols]; for (int j = 0; j < cols; j++) { if (j == 0) { copyPath[j] = *(arrPath + j); } if (j>0) { copyPath[j] = *(arrPath + 0 * cols + j) + copyPath[j-1]; } } for (int i = 1; i < rows; i++) { for (int j = 0; j < cols; j++) { if (j == 0) { copyPath[j] = copyPath[j] + *(arrPath + i*cols + j); } else { //int tempValue = minOfTwoNum(copyPath[j], copyPath[j - 1]); copyPath[j] = minOfTwoNum(copyPath[j], copyPath[j - 1]) + *(arrPath + i*cols + j); } } } result = copyPath[cols - 1]; delete[] copyPath; return result; }
//申请一个一维数组空间,且取rows,cols更小的那个 int MinPathRoad3(int* arrPath, const int rows, const int cols) { //=============步子不能迈太大,先从if,else入手。然后再去整合到一起。 if (arrPath == nullptr || rows < 0 || cols < 0) { throw new std::exception("Invalid para"); } if (rows == 0 || cols == 0) { return 0; } ////分配一行,cols个元素,按行扫描 if (rows >= cols) { int result = 0; //用一个一维数组来存储临时变量 int* copyPath = new int[cols]; for (int j = 0; j < cols; j++) { if (j == 0) { copyPath[j] = *(arrPath + j); } if (j>0) { copyPath[j] = *(arrPath + 0 * cols + j) + copyPath[j - 1]; } } for (int i = 1; i < rows; i++) { for (int j = 0; j < cols; j++) { if (j == 0) { copyPath[j] = copyPath[j] + *(arrPath + i*cols + j); } else { //int tempValue = minOfTwoNum(copyPath[j], copyPath[j - 1]); copyPath[j] = minOfTwoNum(copyPath[j], copyPath[j - 1]) + *(arrPath + i*cols + j); } } } result = copyPath[cols - 1]; delete[] copyPath; //不要删太早了 return result; } else { ////分配一列,rows个元素,按列扫描 int result = 0; //用一个一维数组来存储临时变量 int* copyPath = new int[rows]; for (int i = 0; i < rows; i++) { if (i == 0) { copyPath[i] = *(arrPath + i); } if (i>0) { copyPath[i] = *(arrPath + i * cols ) + copyPath[i - 1]; } } for (int j = 1; j < cols; j++) { for (int i = 0; i < rows; i++) { if (i == 0) { copyPath[i] = copyPath[i] + *(arrPath + i*cols + j); } else { copyPath[i] = minOfTwoNum(copyPath[i], copyPath[i - 1]) + *(arrPath + i*cols + j); } } } result = copyPath[rows - 1]; delete[] copyPath; //不要删太早了 return result; } }
//申请一个一维数组空间,且取rows,cols更小的那个。不使用if,else,而是整合rows,cols判断 int MinPathRoad4(int* arrPath, const int rows, const int cols) { if (arrPath == nullptr || rows < 0 || cols < 0) { throw new std::exception("Invalid para"); } if (rows == 0 || cols == 0) { return 0; } bool bRowMore = (rows>cols);//rows是否更大 int result = 0; //用一个一维数组来存储临时变量 int less = minOfTwoNum(rows, cols); int more = MaxOfTwoNum(rows, cols); int* copyPath = new int[less]; copyPath[0] = *(arrPath); for (int i = 1; i < less; i++) { //bRowMore ? arr[j][0] : arr[0][j] copyPath[i] = copyPath[i - 1] + (bRowMore ? *(arrPath + i) : *(arrPath + i * cols)); }// for (int i = 1; i < more; i++) { for (int j = 0; j < less; j++) { if (j == 0) { copyPath[0] = copyPath[0] + (bRowMore ? *(arrPath + i*cols+j) : *(arrPath + i)); } else { int tempValue = minOfTwoNum(copyPath[j], copyPath[j - 1]); copyPath[j] = minOfTwoNum(copyPath[j], copyPath[j - 1]) + (bRowMore ? *(arrPath + i*cols + j) : *(arrPath + i*rows + j)); } } } result = copyPath[less - 1]; delete[] copyPath; return result; }
一定不能省掉测试用例。
用足够丰富的测试用例,去验证代码功能的完整性。
//====================测试用例================= // 1 3 5 9 // 8 1 3 4 // 5 0 6 1 // 8 8 4 0 void test1() { cout << "Test1================expected:12" << endl; int arrPath[4][4] = { { 1, 3, 5, 9 }, { 8, 1, 3, 4 }, { 5, 0, 6, 1 }, { 8, 8, 4, 0 } }; cout << MinPathRoad((int *)arrPath, 4, 4) << endl; cout << MinPathRoad2((int *)arrPath, 4, 4) << endl; cout << MinPathRoad3((int *)arrPath, 4, 4) << endl; cout << MinPathRoad4((int *)arrPath, 4, 4) << endl; } // // 1 1 1 // 3 4 2 // 7 8 3 // 6 6 0 void test2() { cout << "Test2================expected:8" << endl; int arrPath[4][3] = {{ 1,1,1 }, { 3,4,2 }, { 7,8,3 }, {6,6,0 } }; cout << MinPathRoad((int *)arrPath, 4, 3) << endl; cout << MinPathRoad2((int *)arrPath, 4, 3) << endl; cout << MinPathRoad3((int *)arrPath, 4, 3) << endl; cout << MinPathRoad4((int *)arrPath, 4, 3) << endl; } // // 1 1 2 // 1 4 2 // 2 8 3 // 3 1 0 void test3() { cout << "Test3================expected:8" << endl; int arrPath[4][3] = { { 1, 1, 2 }, { 1, 4, 2 }, {2, 8, 3 }, { 3, 1, 0 } }; cout << MinPathRoad((int *)arrPath, 4, 3) << endl; cout << MinPathRoad2((int *)arrPath, 4, 3) << endl; cout << MinPathRoad3((int *)arrPath, 4, 3) << endl; cout << MinPathRoad4((int *)arrPath, 4, 3) << endl; } // // 1 2 3 4 // 5 0 7 8 // 9 0 11 12 // 0 14 0 0 void test4() { cout << "Test4================expected:14" << endl; int arrPath[4][4] = { { 1, 2, 3,4 }, { 5,0,7,8}, { 9,0,11,12 }, {0,14, 0,0 } }; cout << MinPathRoad((int *)arrPath, 4, 4) << endl; cout << MinPathRoad2((int *)arrPath, 4, 4) << endl; cout << MinPathRoad3((int *)arrPath, 4, 4) << endl; cout << MinPathRoad4((int *)arrPath, 4, 4) << endl; } // // 1 1 1 1 // 1 1 1 1 // 1 1 1 1 // 1 1 1 1 void test5() { cout << "Test5================expected:7" << endl; int arrPath[4][4] = { { 1, 1, 1, 1 }, { 1, 1, 1, 1 }, { 1, 1, 1, 1 }, { 1, 1, 1, 1 } }; cout << MinPathRoad((int *)arrPath, 4, 4) << endl; cout << MinPathRoad2((int *)arrPath, 4, 4) << endl; cout << MinPathRoad3((int *)arrPath, 4, 4) << endl; cout << MinPathRoad4((int *)arrPath, 4, 4) << endl; } // // 0 0 0 0 // 0 1 1 0 // 0 0 0 1 // 0 1 0 0 void test6() { cout << "Test6================expected:0" << endl; int arrPath[4][4] = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }; cout << MinPathRoad((int *)arrPath, 4, 4) << endl; cout << MinPathRoad2((int *)arrPath, 4, 4) << endl; cout << MinPathRoad3((int *)arrPath, 4, 4) << endl; cout << MinPathRoad4((int *)arrPath, 4, 4) << endl; } int main() { test1(); test2(); test3(); test4(); test5(); test6(); system("pause"); return 0; }