二維陣列中的最長遞減子序列
阿新 • • 發佈:2019-02-12
給定一個如下的二維陣列a[][]
1 3 5 7 4
2 1 8 6 5
4 0 -1 -2 6
求其中的最長遞減子序列:7, 5, 3, 1, 0, -1, -2,長度為7。子序列只能朝向上下左右四個方向,不能朝對角線方向。
思路:
該題一看感覺可以用動態規劃做,但是下標不確定從哪裡開始算起,因為有上下左右四個方向,沒有辦法順序計算。只有用遞迴的方法來做。用一個函式FindMax(i, j)來表示以該位置起始的最長遞減子序列的長度,那麼只要取FindMax(i-1, j)、FindMax(i+1, j)、FindMax(i, j-1)、FindMax(i, j+1)中的最大值,然後加1即可,當然前提是上下左右的元素比(i, j)的元素小。
程式碼如下:
#include <iostream> #include <assert.h> using namespace std; int Max(int a, int b, int c, int d) { a = (a > b) ? a : b; a = (a > c) ? a : c; return (a > d) ? a : d; } int FindMax(int a[], int res[], bool flag[], int m, int n, int i, int j) { int umax = 0, dmax = 0, lmax = 0, rmax = 0; //當前元素的下標 int cur = i * n + j; //上面的元素 int up = (i - 1) * n + j; //如果存在上面的元素,並且當前元素大於上面元素,就計算上面元素的最長路徑 if (i > 0 && a[cur] > a[up]) { //如果上面元素的標記為false,說明上面元素的最長路徑還沒有計算 if (!flag[up]) { //計算上面元素的最長路徑,計算結束後將上面元素的標記賦值為true umax = FindMax(a, res, flag, m, n, i-1, j); res[up] = umax; flag[up] = true; } //如果上面元素的標記為true,說明已經計算過,直接取結果 else umax = res[up]; } //下面的元素 int down = (i + 1) * n + j; if (i < m-1 && a[cur] > a[down]) { if (!flag[down]) { dmax = FindMax(a, res, flag, m, n, i+1, j); res[down] = dmax; flag[down] = true; } else dmax = res[down]; } //左邊的元素 int left = i * n + j - 1; if (j > 0 && a[cur] > a[left]) { if (!flag[left]) { lmax = FindMax(a, res, flag, m, n, i, j-1); res[left] = lmax; flag[left] = true; } else lmax = res[left]; } //右邊的元素 int right = i * n + j + 1; if (j < n-1 && a[cur] > a[right]) { if (!flag[right]) { rmax = FindMax(a, res, flag, m, n, i, j+1); res[right] = rmax; flag[right] = true; } else rmax = res[right]; } //當上下左右元素的值都計算完,就可以計算當前元素的最長路徑了 flag[cur] = true; res[cur] = 1 + Max(umax, dmax, lmax, rmax); return res[cur]; } int LDS(int a[], int m, int n) { assert(a != NULL && m > 0 && n > 0); int* res = new int[m * n]; memset(res, 0, m*n*sizeof(int)); bool* flag = new bool[m * n]; memset(flag, false, m*n*sizeof(bool)); int max = 0; //計算以每個元素起始的最長路徑長度,並記錄全域性的最長路徑長度max for (int i = 0; i < m; ++i) { for (int j = 0; j < n; ++j) { int cur = i * n + j; if (!flag[cur]) FindMax(a, res, flag, m, n, i, j); if (max < res[cur]) max = res[cur]; } } delete [] res; delete [] flag; return max; } void main() { int a[] = { 100,100,18,19,20, 10,100,16,100,14, 12,14,15,100,12, 100,100,11,100,11, 100,100,9,100,7}; int m = 5, n = 5; cout << LDS(a, m, n) << endl; }