《程序員代碼面試指南--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; }