最长上升子序列模型

Posted on 2022-05-08 22:00  ZheyuHarry  阅读(28)  评论(0编辑  收藏  举报

###这里即将要介绍的是算法提高课中最长上升子序列的一些模型和题解###

 

 

###题目一:怪盗基德的滑翔翼 ### 1017. 怪盗基德的滑翔翼 - AcWing题库

题面介绍:就是有N幢高度已知的楼,从任意一个楼开始,允许向右向左任一方向向下滑翔,求能滑到的最多的楼房数。

 

分析:其实就是求问这个长度为N的序列中,最长的下降子序列的长度,所以我们可以根据算法基础课中的线性DP直接得出。

但是,这里有一点需要注意的是,可以向左或向右滑翔,所以也应该定义一个f2,从右往左寻找最长上升子序列长度。

 

状态表示:f1[i]表示的是从1~i的最长上升子序列的长度 , f2[i]表示从n~i的最长上升子序列的长度。

状态转移方程:f1[i] = max(f1[i] , f1[j] + 1) ,  1 <= j < i;   f2[i] = max(f2[i] , f2[j] + 1 ) , i < j <= n;

 

核心代码:

 

for(int i = 1;i<=n;i++) f1[i] = 1 , f2[i] = 1;
for(int i = 1;i<=n;i++)
{
int cnt = 0;
for(int j = 1;j<=i;j++)
if(q[j] < q[i])
{
f1[i] = max(f1[i] , f1[j] + 1);
}
}

for(int i = n;i>=1;i--)
{
int cnt = 0;
for(int j = n;j>=i;j--)
if(q[j] < q[i])
{
f2[i] = max(f2[i] , f2[j] + 1);
}
}
int res = 0;
for(int i = 1;i<=n;i++)
{
res = max(res,f1[i]);
res = max(res,f2[i]);
}
cout << res << '\n';

 

 

 

###题目二:登山 ### 1014. 登山 - AcWing题库

 

题面介绍:先往上走,再往下走,问能经过的景点的个数最多的是什么。

 

分析:整体思路和前面一样,也是前后两个方向找最长上升子序列 , 但是最后是把当前这个点的最长上升子序列和最长下降子序列之和放在一起找最大值。

 

 

状态表示:f1[i]表示的是从1~i的最长上升子序列的长度 , f2[i]表示从n~i的最长上升子序列的长度。

状态转移方程:f1[i] = max(f1[i] , f1[j] + 1) ,  1 <= j < i;   f2[i] = max(f2[i] , f2[j] + 1 ) , i < j <= n;

 

核心代码:

for(int i = 1;i<=n;i++) f1[i] = 1 , f2[i] = 1;
for(int i = 1;i<=n;i++)
{
int cnt = 0;
for(int j = 1;j<=i;j++)
if(q[j] < q[i])
{
f1[i] = max(f1[i] , f1[j] + 1);
}
}

for(int i = n;i>=1;i--)
{
int cnt = 0;
for(int j = n;j>=i;j--)
if(q[j] < q[i])
{
f2[i] = max(f2[i] , f2[j] + 1);
}
}
int res = 0;
for(int i = 1;i<=n;i++)
{
res = max(res,f1[i] + f2[i] - 1);
}
cout << res << '\n';

 

 

###题目三:合唱队形 ### 482. 合唱队形 - AcWing题库

 

题面介绍:和前面的登山很相似,但是问的是有哪些人要出队(哪些景点不去);

 

分析:就是说要形成一个上升序列接下降序列需要去掉多少个人,因为人的总数不变,所以问题转化为上升序列和下降序列之和的最大值是什么,所以又是一个求最长上升子序列的问题。

 

状态表示:f1[i]表示的是从1~i的最长上升子序列的长度 , f2[i]表示从n~i的最长上升子序列的长度。

状态转移方程:f1[i] = max(f1[i] , f1[j] + 1) ,  1 <= j < i;   f2[i] = max(f2[i] , f2[j] + 1 ) , i < j <= n;

 

核心代码:

for(int i = 1;i<=n;i++) f1[i] = 1 , f2[i] = 1;
for(int i = 1;i<=n;i++)
{
int cnt = 0;
for(int j = 1;j<=i;j++)
if(q[j] < q[i])
{
f1[i] = max(f1[i] , f1[j] + 1);
}
}

for(int i = n;i>=1;i--)
{
int cnt = 0;
for(int j = n;j>=i;j--)
if(q[j] < q[i])
{
f2[i] = max(f2[i] , f2[j] + 1);
}
}
int res = 0;
for(int i = 1;i<=n;i++)
{
res = max(res,f1[i] + f2[i] - 1);
}
cout << n - res << '\n';

 

 

###题目四:友好城市 ### 1012. 友好城市 - AcWing题库

 

题面介绍:在两个一一配对的序列a,b中建边,要求这些边不能交叉(端点重合也不行),问最多的边能有多少。

 

分析:也是要求一个最长上升子序列,只是这里我们假设两个序列的值分别为x, y,我们是要x1 > x , y1 > y,这样去找。

 

状态表示:f[i]表示的是 a[i]这对配对的一定要建边的情况下,所能建边的最大值。

状态转移:f[i] = max(f[j] + 1 , f[i]) , 1 <= j <i  && b[j] < b[i]

 

核心代码:

for(int i = 1;i<=n;i++) cin >> q[i].x >> q[i].y;

sort(q+1,q+n+1);
for(int i = 1;i<=n;i++) f[i] = 1;
for(int i = 1;i<=n;i++)
{
for(int j = i - 1;j>=1;j--)
{
if(q[j].y < q[i].y) {
f[i] = max(f[i] , f[j] + 1);
}
}
}

int res = 0;
for(int i = 1;i<=n;i++) res = max(res , f[i]);
cout << res << '\n';