最长上升子序列【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
*/