LIS教学篇
Posted 酱油党gsh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LIS教学篇相关的知识,希望对你有一定的参考价值。
LIS是最长上升子序列,(递增子序列是指,子序列的元素是递增的)例如:5 1 6 8 2 4 5 10,最长递增子序列是1 2 4 5 10。
对于LIS,有两种解法,一种是比较容易想到的O(n^2)的解法,我们用N[i]表示第i个数,dp[i]表示第i个数所在子序列中最大的长度(比如对于序列{1,2,5,4,2},那dp={1,2,3,3,2}).那么我们只要枚举j从1到i-1,且N[j]<N[i],就可以进行更新:dp[i]=min(dp[i],dp[j]+1),这样的复杂度是O(n^2)。
O(n^2)的复杂度往往是不如人意的,我们可以考虑尝试优化,如果我们用dp[i]表示长度为i的子序列的最后一个数的最小值,那么我们可以发现这样的dp数组一定是非递减的,因为如果长度为2的子序列末尾最小值是3,那长度为1的子序列末尾最小值肯定可以取3,是不可能小于3的,因此我们可以先把数组初始化为INF,每次更新的时候二分一下dp数组,找到一个大于等于N[i]的数的位置,然后在这个地方更新。举个例子,对于
5 1 6 8 2 4 5 10这个序列
一开始dp数组为:
INF INF INF INF INF INF INF INF
更新5的时候,在1的位置(下标从1开始)更新,表示长度为1的子序列的最后一个数最小是5,此时dp数组为
5 INF INF INF INF INF INF INF
更新1的时候,二分到1的位置,在1的位置更新,表示长度为1的子序列的最后一个数最小是1,此时dp数组为
1 INF INF INF INF INF INF INF
更新6的时候,二分到2的位置,在2的位置更新,表示长度为2的子序列的最后一个数最小是6,此时dp数组为
1 6 INF INF INF INF INF INF
更新8的时候,二分到3的位置,在3的位置更新,表示长度为3的子序列的最后一个数最小是8,此时dp数组为
1 6 8 INF INF INF INF INF
更新2的时候,二分到2的位置,在2的位置更新,表示长度为2的子序列的最后一个数最小是2,此时dp数组为
1 2 8 INF INF INF INF INF
更新4的时候,二分到3的位置,在3的位置更新,表示长度为3的子序列的最后一个数最小是4,此时dp数组为
1 2 4 INF INF INF INF INF
更新5的时候,二分到4的位置,在4的位置更新,表示长度为4的子序列的最后一个数最小是5,此时dp数组为
1 2 4 5 INF INF INF INF
更新10的时候,二分到5的位置,在5的位置更新,表示长度为5的子序列的最后一个数最小是10,此时dp数组为
1 2 4 5 10 INF INF INF
这时候dp数组处理完毕,5为LIS长度。
可以在这里测试下代码:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1134
代码如下:
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <vector> #include <queue> #include <string> #include <stack> #include <map> #include <set> #include <bitset> #define X first #define Y second #define clr(u,v); memset(u,v,sizeof(u)); #define in() freopen("data","r",stdin); #define out() freopen("ans","w",stdout); #define Clear(Q); while (!Q.empty()) Q.pop(); #define pb push_back using namespace std; typedef long long ll; typedef pair<int, int> pii; const int maxn = 1e5 + 10; const int INF = 0x3f3f3f3f; int c[maxn];//c[i]代表序列长度为i时的最小数字 int main() { int n, x; scanf("%d", &n); clr(c, 0x3f);//一开始全部初始化为INF for (int i = 0; i < n; i++) { scanf("%d", &x); int pos = lower_bound(c + 1, c + 1 + n, x) - c;//找到c中第一个大于等于x的数字的下标 // for (int j = 1; j <= n; j++) // printf("%d ", c[j]); // puts(""); // printf("pos : %d\n", pos); c[pos] = min(c[pos], x); } printf("%d\n", lower_bound(c + 1, c + 1 + n, INF) - (c + 1)); return 0; }
以上是关于LIS教学篇的主要内容,如果未能解决你的问题,请参考以下文章