線性DP LIS淺談
阿新 • • 發佈:2018-11-14
LIS問題
什麼是LIS?
百度百科
最長上升子序列(Longest Increasing Subsequence,LIS),在電腦科學上是指一個序列中最長的單調遞增的子序列。
怎麼求LIS?
O(n^2)做法
具體做法是用兩個for,狀態轉移方程為f[i]=max(f[i],f[j]+1)其中f陣列為這個位置的LIS長度,然後用max找一下最長LIS即可
程式碼
for(int i=1;i<=n;i++) for(int j=1;j<i;j++) if(a[j]<a[i]) f[i]=max(f[i],f[j]+1); for(int i=1;i<=n;i++) ans2=max(ans2,f[i]);
其中也可以取等號
O(nlogn)做法
具體做法是用陣列去維護LIS,然後如果遇到比他小的二分陣列
程式碼
if(u[ans2]<a[i])
u[++ans2]=a[i];
else
u[lower_bound(u+1,u+ans2+1,a[i])-u]=a[i];
拓展定理(Dilworth定理)
什麼是Dilworth定理?
百度百科
反鏈是一種偏序集,其任意兩個元素不可比;而鏈則是一種任意兩個元素可比的偏序集。Dilworth定理說明,存在一個反鏈A與一個將序列劃分為鏈族P的劃分,使得劃分中鏈的數量等於集合A的基數。當存在這種情況時,對任何至多能包含來自P中每一個成員一個元#### 素的反鏈,A一定是此序列中的最大反鏈。同樣地,對於任何最少包含A中的每一個元素的一個鏈的劃分,P也一定是序列可以劃分出的最小鏈族。偏序集的寬度被定義為A與P的共同大小。
另一種Dilworth定理的等價表述是:在有窮偏序集中,任何反鏈最大元素數目等於任何將集合到鏈的劃分中鏈的最小數目。一個關於無限偏序集的理論指出,在此種情況下,一個偏序集具有有限的寬度w,當且僅當它可以劃分為最少w條鏈。
對於LIS的用途
求LIS的個數就是求最長下降子序列的長度,注意其中的<之類的符號是取補集的,也就是說如果上面用的>那麼反面就是<=
完整程式碼(洛谷模板題 P1020 導彈攔截)
O(nlogn)演算法
#include <bits/stdc++.h> using namespace std; int a[100005],u[100005],l[100005]; bool cmp(int a,int b) { return a>b; } int main() { ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); int n=1; while(cin>>a[n]) n++; n--; int ans1=1,ans2=1; l[1]=u[1]=a[1]; for(int i=2;i<=n;i++) { if(l[ans1]>=a[i]) l[++ans1]=a[i]; else l[upper_bound(l+1,l+ans1+1,a[i],cmp)-l]=a[i]; if(u[ans2]<a[i]) u[++ans2]=a[i]; else u[lower_bound(u+1,u+ans2+1,a[i])-u]=a[i]; } cout<<ans1<<" "<<ans2; }
O(n^2)演算法
#include <bits/stdc++.h>
using namespace std;
int a[100010];
int f[100010];
int main()
{
int n=1;
while(cin>>a[n])
f[n++]=1;
n--;
int ans1,ans2;
ans1=ans2=-1;
for(int i=1;i<=n;i++)
for(int j=1;j<i;j++)
if(a[j]>=a[i])
f[i]=max(f[i],f[j]+1);
for(int i=1;i<=n;i++)
ans1=max(ans1,f[i]);
for(int i=1;i<=n;i++)
f[i]=1;
for(int i=1;i<=n;i++)
for(int j=1;j<i;j++)
if(a[j]<a[i])
f[i]=max(f[i],f[j]+1);
for(int i=1;i<=n;i++)
ans2=max(ans2,f[i]);
cout<<ans1<<" "<<ans2;
}