POJ2533 DP入門級題目-最大上升子序列(LIS)-O(n^2)與O(nlogn) (變形,POJ1631)
阿新 • • 發佈:2019-02-16
0
O(nlogn)複雜度的 方法2)很重要,要掌握。
1
1)時間複雜度為O(N^2),原理是不斷更新每個元素作為最後一個元素的各自序列的長度
#include <iostream>//入門DP問題,時間複雜度O(N^2) using namespace std; int main() { int a[1010];//輸入元素 int d[1010];//d[i]代表以a[i]為最後一個元素的序列的長度 int n;cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]; d[i]=1; } int maxn=0; for(int i=1;i<=n;i++){ for(int j=1;j<=i-1;j++){ if(a[j]<a[i]&&d[i]<d[j]+1){//動態更新每一個元素作為最後一個元素的所構造的序列的長度 d[i]=d[j]+1; } } if(d[i]>maxn) maxn=d[i];//順便記錄最大值 } cout<<maxn<<endl; }
2)時間複雜度為O(nlogn),構造一個數組d用來儲存一個近似的最大上升子序列(不一定是真正的最長子序列,但是長度與最長子序列相同,因為每一次更新替換序列裡某個數時,長度不變,序列潛力變大),用二分法查詢序列裡的數則降低了時間複雜度。
(即一個一個元素插入單調遞增的佇列,如果發現後者比當前的隊尾元素大那麼插入隊尾,否則二分查詢到比他大的最小值那個位置,替換掉即可。)
#include <iostream> #include <cstdio> using namespace std; int len; int a[1010],d[1010]; int update(int k) { int l=1,r=len; int mid; while(l<=r){ mid=(l+r)/2; if(d[mid]<k){ l=mid+1; } else if(d[mid]>k){ r=mid-1; } else{ return mid; } } return l;//返回 l 和 返回 r 是不一樣的,注意! } int main() { int n;cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]; } len=1;d[1]=a[1]; for(int i=2;i<=n;i++){ int position=update(a[i]); d[position]=a[i]; if(position>len) len++; } cout<<len<<endl; return 0; }
3)疑惑???為什麼同樣用構造一個數組來儲存近似的最大上升子序列時,但不用二分法搜,倒序遍歷就會WA、??,原理同2)一樣,不過時間複雜度變成O(n^2)為什麼就過不了呢?1)中也是O(n^2)但是過了呀!
求解釋,0.0
#include <iostream> #include <cstdio> using namespace std; int len; int a[1010],d[1010]; int update(int k) { for(int j=len;j>0;j--){ if(d[j]<k){ return j+1; } } } int main() { int n;cin>>n; for(int i=1;i<=n;i++){ cin>>a[i]; } len=1;d[1]=a[1]; for(int i=2;i<=n;i++){ int position=update(a[i]); d[position]=a[i]; if(position>len) len++; } for(int k=1;k<=len;k++) cout<<d[k]<<" "; return 0; }
#include <iostream>
using namespace std;
int main()
{
int a[1010],d[1010];
int n;cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
int top=1;d[1]=a[1];
for(int i=2;i<=n;i++){
if(d[top]<a[i]){//擴充套件序列長度
d[++top]=a[i];
}
else{//更新已經選進序列的字元,使改序列保持最大潛力
for(int j=top-1;j>0;j--){
if(d[j]<a[i]){
d[j+1]=a[i];
break;
}
}
}
}
cout<<top<<endl;
return 0;
}