演算法學習之尋找最長等差數列
阿新 • • 發佈:2018-12-31
最長等差數列就是在一個數組中,組成等差數列的最長的那一個,首先我們對陣列排序,然後我們一般會先想到暴力法從第一個開始迴圈遍歷整個陣列,時間複雜度O(N^3),下面給出虛擬碼
int i,j,k,len = 0 for(i = 0; i < n; i++) for(j = i+1; j < n; j++) { int delta = a[j] - a[i]; len = 2; int tmp = a[j]; for(k = j+1; k < n; k++) { if(a[k] - tmp== delta) { len++; tmp = a[k]; } } 求出最大的len }
第二種方法用動態規劃,我們可以利用等差數列的性質來做,當等差數列長度為奇數的時候,比如相鄰3個數,aiajak
則2*aj = ai+ak;只要任何一個關於它對稱的兩個數相加都等於它的2倍,有了這個性質我們就可以利用動態規劃來做
dp[i][j] = d,表示以a[i]a[j]為前兩個元素的等差數列長度為d,我們再來推狀態轉移方程,
如果ai+ak<2*aj,說明ak - aj < aj - ai dp[i][j]的值不能確定,k接著往後走
如果ai+ak>2*aj,說明ak-aj > aj -ai,此時dp[i][j]的值為2,因為k不能往後走了,再往後走二者差距更大,所以a[i]a[j]開頭的等差數列為2,然後再i--,開始尋找下個ai aj開頭的等差數列
如果ai+ak==2*aj,則說明這三個數構成等差數列,此時dp[i][j]=dp[j][k]+1;也就是說以ajak開頭的等差數列前加上了一個ai,狀態轉移確定好後,下面開始寫程式碼
#include <stdio.h> #include <stdlib.h> #include <string.h> struct result { int max_len; int i; int j; }; int max(int a,int b) { return a > b ? a : b; } void find_llap(int *arry,int n) { int dp[50][50]; //dp[i][j]代表以arry[i]和arry[j]為頭兩個元素的等差數列,此題規模小,規模大的時候i,j可以做hash,就可以節省空間 int i,j,k; struct result result; memset(dp,0,sizeof(dp)); for(i = 0; i < n; i++) { dp[i][n] = 2; //因為以arry[n]為第二個元素的等差數列長度一定為2 } if(n < 2) { for(i = 0; i < 2; i++) printf("%d ",arry[i]); return; } result.max_len = 0; for(j = n-1; j >= 1; j--) { i = j-1;k = j+1; while(i >= 0 && k <= n) { if(arry[i]+arry[k] > arry[j]<<1) //如果和ai+ak>2aj,說明ak-aj>aj-ai,這個構造不成等差數列,則k以後的更不行,因為陣列是升序 { dp[i][j] = 2; //所以此步驟直接賦值為2,及只有ai aj構造成等差數列 i--; } else if(arry[i]+arry[k] < arry[j]<<1) { k++; //如果ai+ak<2aj,說明ak-aj<aj-ai,所以以ai aj開頭的等差數列,往後遍歷,可能還有新的元素符合公差,所以k++ } else { /*如果ai+ak=2aj,則正好構成等差數列,直接將前一個子問題的解+1即可*/ dp[i][j] = dp[j][k]+1; result.max_len = max(result.max_len,dp[i][j]); if(result.max_len == dp[i][j]) { result.i = i; result.j = j; } i--;k++; } } if(result.max_len == 0) { result.max_len = 2; result.i = 0; result.j = 1; } while(i >= 0) /*這個迴圈是因為k已經到最後了,所以以這個j為第二個元素的所有數列都是2*/ { dp[i][j] = 2; i--; } } int delta = arry[result.j] - arry[result.i]; int last = arry[result.i]+(result.max_len-1)*delta; for(i = arry[result.i]; i <= last; i+=delta) printf("%d ",i); } int main() { int n; int arry[100]; scanf("%d",&n); int i; for(i = 0; i < n; i++) scanf("%d",&arry[i]); find_llap(arry,n-1); return 0; }
依據這個性質,還有一種動態規劃,dp[i][j]代表等差數列最後兩個元素,然後從前往後遍歷,得到的結果一樣