最长上升子序列【LIS】
Input
14
13 7 9 16 38 24 37 18 44 19 21 22 63 15
Output
max=8
7 9 16 18 19 21 22 63
光求长度很容易,但记录路径就麻烦了(至少我想一下午没想出来)
解析:
- 记录长度很容易,从前往后扫一遍,记录每次 1~i 的最大长度
- 于此同时,另开一个数组结构体,(或一个二维数组)记录路径,为了复制方便,推荐用结构体哦
#include<stdio.h> #include<algorithm> using namespace std; struct qu{ int l[101]; }line[101]; int a[101],f[101]; int main() { int n,aim,ans=1; scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%d",&a[i]); for(int i=1;i<=n;++i) { f[i]=1; line[i].l[1]=a[i]; for(int j=1;j<i;++j) { if(a[j]<a[i] && f[j]+1>f[i]) { f[i]=f[j]+1; line[i]=line[j]; line[i].l[f[i]]=a[i]; } } if(ans<f[i]) { ans=f[i]; aim=i; } } printf("max=%d ",ans); for(int i=1;i<=ans;++i) printf("%d ",line[aim].l[i]); return 0; }
updated:2018-11-04
nlogn 做法
f[i] 记录长度为 i 结尾最小的数,二分查找替换
#include<stdio.h> #include<algorithm> using namespace std; int n,a[1010],f[1010]; int main() { scanf("%d",&n); for(int i=1;i<=n;++i) { scanf("%d",&a[i]); } f[1]=a[1]; int len=1; for(int i=2;i<=n;++i) { int l=0,r=len,mid; if(a[i]>f[len]) f[++len]=a[i]; else { while(l<r) { mid=(l+r)>>1; if(f[mid]>a[i]) r=mid; //大了 else l=mid+1; } f[l]=min(a[i],f[l]); } } printf("%d",len); return 0; }
-------------------------------------------------------分割线
最长公共子序列【LCS】
Input
5
3 2 1 4 5
1 2 3 4 5
Output
3
回想当年学DP的心路历程(抄代码),现在觉得刚能看懂 DP 就要退役了
解析:
最基本的O(n2)做法,f[i][j] 表示A序列匹配到i位,B序列匹配到j位的最长公共子序列
思路很简单,如果当前匹配:f[i][j]=f[i-1][j-1]+1;
否则接上之前的答案:f[i][j]=max(f[i-1][j],f[i][j-1]);
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; const int N=10001; int a[N],b[N],f[N][N]; int main() { int n; scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%d",&a[i]); for(int i=1;i<=n;++i) scanf("%d",&b[i]); for(int i=1;i<=n;++i) { for(int j=1;j<=n;++j) { if(a[i]==b[j]) f[i][j]=f[i-1][j-1]+1; else f[i][j]=max(f[i-1][j],f[i][j-1]); } } printf("%d",f[n][n]); return 0; }
O(nlogn) 做法需要新建立一个映射关系
将A序列的值对应的下标映射,然后我们通过扫描B序列的值,可以得到B在A序列中的位置关系,如果上升,则公共
于是问题转化为了求LIS
code
#include<stdio.h> #include<algorithm> using namespace std; const int MX=1e5+1; int yins[MX],a[MX],b[MX],f[MX]; int main() { int n; scanf("%d",&n); for(int i=1;i<=n;++i) { scanf("%d",&a[i]); yins[a[i]]=i;f[i]=0x3f3f3f3f; } for(int i=1;i<=n;++i) scanf("%d",&b[i]); int len=0; for(int i=1;i<=n;++i) { int l=0,r=len; if(yins[b[i]]>f[len]) f[++len]=yins[b[i]]; else { while(l<r) { int mid=(l+r)>>1; if(yins[b[i]]<f[mid]) r=mid; else l=mid+1; } f[l]=min(f[l],yins[b[i]]); } } printf("%d",len); return 0; }
-------------------------------------------------------分割线
最长公共上升子序列【LCIS】
Input
5
1 4 2 5 -12
4
-12 1 2 4
Output
2
这是xing哥哥当时布置的作业(一直没做……关键当时不会呀)
解析:
O(n3)做法很好理解,A、B中找到相同的元素,往B前扫一遍,找到一个能接的上的最长的……
更新答案就好了
code
#include<stdio.h> #include<algorithm> using namespace std; int n,m,a[1010],b[1010],f[1010][1010]; int main() { scanf("%d",&n); for(int i=1;i<=n;++i) { scanf("%d",&a[i]); } scanf("%d",&m); for(int i=1;i<=m;++i) { scanf("%d",&b[i]); } for(int i=1;i<=n;++i) { for(int j=1;j<=m;++j) { if(a[i]==b[j]) { for(int k=0;k<j;++k) { if(a[i]>b[k] && f[i-1][k]+1>f[i][j]) { f[i][j]=f[i-1][k]+1; } } } else f[i][j]=f[i-1][j]; } } printf("%d",f[n][m]); return 0; } /* 5 1 4 2 5 -12 4 -12 1 2 4 */