有一个n*n的矩阵WW[n][n],存储正整数 int WW[4][4] = {1,3,5,9,2,1,3,4,5,2,6,7,6,8,4,3}; ,棋子从左上角出发,到右下角,求通过的最短路径。矩阵中每一个数值表明距离的长度。算法
分析:从[0][0]到[n-1][n-1],每一个阶段都有两种决策,向下或向右。code
一条路走到黑,只选择下一步中较小值。blog
#define N 4 int minDistGreedy(int w[N][N]) { int result = w[0][0]; int i = 0, j = 0; while(i < N && j < N) { if(w[i+1][j] < w[i][j+1]) // 向下 { result += w[i+1][j]; ++i; if (i == N-1 && j < N-1) // 向下走完,但右边没走完 { while(j < N-1) { result += w[i][j+1]; ++j; } break; } } else { result += w[i][j+1]; // 向右 ++j; if (j == N-1 && i < N-1) // 向右走完,但下边没走完 { while(i < N-1) { result += w[i+1][j]; ++i; } break; } } } return result; } int main() { int WW[4][4] = {1,3,5,9,2,1,3,4,5,2,6,7,6,8,4,3}; cout << "贪心算法求的最短距离:" << minDistGreedy(WW) << endl; // 19 system("pause"); return 0; }
总共要走2*(n-1)步递归
#define N 4 int minDist = 65535; void minDistBT(int i, int j, int dist, int w[4][4], int n) { if (i == n-1 && j == n-1) { if (dist+w[i][j] < minDist) // 此处对最后一个数,进行了特殊处理。由于递归没有处理到 { minDist = dist+w[i][j]; } return; } if (i < n) { minDistBT(i+1, j, dist+w[i][j], w, n); } if(j < n) { minDistBT(i, j+1, dist+w[i][j], w, n); } } int main() { int WW[4][4] = {1,3,5,9,2,1,3,4,5,2,6,7,6,8,4,3}; minDistBT(0,0,0,WW,4); cout << minDist << endl; // 输出19 system("pause"); return 0; }
关于重复计算的步数,能够经过添加【备忘录】的方式,省去递归。即已经算过的,就不用再计算了。class
f(i,j,dist); // i,j 表示位置,dist 表示当前所走的路径长度。
找到公式:min_dist(i, j) = w[i][j] + min(min_dist(i, j-1), min_dist(i-1, j)) 最短路径等于当前值加上左边或上边的较小值。bfc
有了回溯代码以后,画出递归树,找到重复子问题,即重复走过的位置(i,j)相等,但距离不等,取最小。im
#define N 4 int minDistDP(int w[4][4]) { int states[N][N] = {0}; int sum = 0; for (int j = 0; j < N; ++j) // 初始化states的第一行数据 { sum += w[0][j]; states[0][j]=sum; } sum = 0; for (int i = 0; i < N; ++i) // 初始化states的第一列数据 { sum += w[i][0]; states[i][0] = sum; } for (int i = 1; i < N; ++i) { for (int j = 1; j < N; ++j) { states[i][j] = w[i][j]+min(states[i-1][j], states[i][j-1]); } } return states[N-1][N-1]; } int main() { int WW[4][4] = {1,3,5,9,2,1,3,4,5,2,6,7,6,8,4,3}; cout << "最短距离为:" << minDistDP(WW) << endl; // 19 system("pause"); return 0; }