木棍加工——線性dp、dilworth定理、最長上升子序列
阿新 • • 發佈:2022-04-04
P1233 木棍加工 - 洛谷 | 電腦科學教育新生態 (luogu.com.cn)
讀題可知我們要求的是將所有木棍分割成長度與寬度都是不上升子序列的序列個數。
既然是不上升子序列,那麼我們就結構體排序一下。
1 bool cmp(const node&s1,const node&s2) 2 { 3 if(s1.l==s2.l)return s1.w>s2.w; 4 return s1.l>s2.l; 5 }
根據dilworth定理:
一個序列要最少分割為多少個不上升子序列的個數等於該序列最長上升子序列的長度。
注意:雖然問題轉化為求最長上升子序列,但是該序列仍應該是排好序的原本序列,而不是再將序列排成從小到大上升的序列進行求值。是在原序列基礎上求的長度!!
那麼問題就轉化為求該序列的最長上升子序列的長度。
方法1:O(n^2)
狀態表示:排序後以第i個數字為終點的最長上升子序列的長度
狀態計算:j從1到i-1遍歷一遍,如果符合上升的要求(j的寬度小於i的寬度)那就f[i]=max(f[j]+1,f[i])。
如果不符合上升的要求,那就continue
問題:為什麼符合上升的要求只需要考慮寬度,不考慮長度呢?
回答:因為咱們總序列的排序就是按照長度由大到小排的,相當於把長度這一維給看作定值,通過求寬度由小到大的最長上升子序列長度來得到答案(這是由dilworth定理推出來的)。
1 #include <bits/stdc++.h> 2View Codeusing namespace std; 3 const int N=5e3+100; 4 struct node 5 { 6 int l,w; 7 }e[N]; 8 int f[N]; 9 bool cmp(const node&s1,const node&s2) 10 { 11 if(s1.l==s2.l)return s1.w>s2.w; 12 return s1.l>s2.l; 13 } 14 int main() 15 { 16 int n;scanf("%d",&n); 17 for(int i=1;i<=n;i++)scanf("%d%d",&e[i].l,&e[i].w); 18 sort(e+1,e+1+n,cmp); 19 20 int ans=0; 21 for(int i=1;i<=n;i++) 22 { 23 f[i]=1; 24 for(int j=1;j<i;j++) 25 { 26 if(e[j].w<e[i].w&&f[i]<f[j]+1) 27 f[i]=f[j]+1; 28 } 29 ans=max(ans,f[i]); 30 } 31 32 printf("%d\n",ans); 33 34 35 return 0; 36 }
方法2:O(nlogn)
狀態表示:排序後以第i個數字為終點的上升子序列結尾的最小值。
狀態計算:用i將序列遍歷一遍,如果第i個結構體的寬度大於f[ans],那麼f[++ans]=第i個結構體的寬度。
否則,就二分求大於等於第i個結構體寬度的最小的那個位置k,f[k]=第i個結構體的寬度。
最後答案求的是最長上升子序列的長度,也就是ans的大小啦。
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N=5e3+100; 4 int f[N],ans; 5 struct node 6 { 7 int l,w; 8 }e[N]; 9 10 bool cmp(const node&s1,const node&s2) 11 { 12 if(s1.l==s2.l)return s1.w>s2.w; 13 return s1.l>s2.l; 14 } 15 16 int main() 17 { 18 int n;scanf("%d",&n); 19 for(int i=1;i<=n;i++)scanf("%d%d",&e[i].l,&e[i].w); 20 21 sort(e+1,e+1+n,cmp); 22 for(int i=1;i<=n;i++) 23 { 24 if(e[i].w>f[ans])f[++ans]=e[i].w; 25 else 26 { 27 int k=lower_bound(f+1,f+1+ans,e[i].w)-f; 28 f[k]=e[i].w; 29 } 30 } 31 32 printf("%d\n",ans); 33 34 return 0; 35 }View Code