LIS定义:
最长上升子序列(Longest Increasing Subsequence,LIS),在计算机科学上是指一个序列中最长的单调递增的子序列。
问题描述:
这类问题一般表述为:给一个数列,长度为n,求LIS长度。
样例输入:
循环每两行输入测试样例,第一行输入n,第二行输入n个数字
9
5 1 3 6 8 2 9 0 10
5
1 2 4 0 2
9
2 7 1 5 6 4 3 8 9
样例输出:
每个测试样例,输出一行LIS长度
6
3
5
O(n^2)方法:
使用动态规划思想,d[i]表示前i个的解,那么就有状态转移方程 d[i] = max{ 1, d[j] + 1 }, j < i, a[j] < a[i].
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = int(2e5 + 5);
// 求LIS的长度O(n^2)
int g_d[N];
int LIS(const int arr[], const int &len)
{
int i, j, ans = 0;
for (i = 0; i < len; i++)
{
g_d[i] = 1;
for (j = 0; j < i; j++)
{
if (arr[j] < arr[i]) g_d[i] = max(g_d[i], g_d[j] + 1);
}
ans = max(ans, g_d[i]);
}
return ans;
}
int main()
{
int n, i;
int a[N];
while (scanf("%d", &n) != EOF)
{
for (i = 0; i < n; i++)
{
scanf("%d", &a[i]);
}
printf("%d\n", LIS(a, n));
}
return 0;
}
O(nlogn)方法:
在O(n^2)方法的第二重循环上使用单调数组维护:如果a[i] > d[len - 1],d[len++] = a[i]; 如果a[i] < d[len - 1],则利用二分查找前len个元素,找到第一个大于等于a[i]的位置k,将其替换d[k] = a[i](如果k后面还有元素,不需要动,这样的巧妙之处就是当a[i + 1]再与d[]末尾元素比较的时候仍是前i个数字的最优答案的最大值,这里的最优答案便是LIS长度不变的情况下,每个元素的值尽量小)
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = int(2e5 + 5);
// 求LIS的长度O(nlogn)
int g_d[N];
int LIS(const int arr[], const int &len)
{
int i, j, k = 0;
g_d[k++] = arr[0];
for (i = 1; i < len; i++)
{
if (arr[i] > g_d[k - 1]) g_d[k++] = arr[i];
else g_d[lower_bound(g_d, g_d + k, arr[i]) - g_d] = arr[i];
}
return k;
}
int main()
{
int n, i;
int a[N];
while (scanf("%d", &n) != EOF)
{
for (i = 0; i < n; i++)
{
scanf("%d", &a[i]);
}
printf("%d\n", LIS(a, n));
}
return 0;
}
O(n^2) 求解LIS长度,并输出LIS串:
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = int(2e5 + 5);
// 求LIS的长度O(n^2), 并输出LIS串
int g_d[N];
int g_pre[N];
int LIS(const int arr[], const int &len, int lisArr[])
{
int i, j, ans = 0, maxId = 0;
for (i = 0; i < len; i++)
{
g_pre[i] = i;
}
for (i = 0; i < len; i++)
{
g_d[i] = 1;
for (j = 0; j < i; j++)
{
if (arr[j] < arr[i] && g_d[i] < g_d[j] + 1)
{
g_d[i] = g_d[j] + 1;
g_pre[i] = j;
}
}
if (ans < g_d[i])
{
ans = g_d[i];
maxId = i;
}
}
j = maxId;
for (i = ans - 1; i >= 0; i--)
{
lisArr[i] = arr[j];
j = g_pre[j];
}
return ans;
}
int main()
{
int n, i;
int a[N], lis[N];
while (scanf("%d", &n) != EOF)
{
for (i = 0; i < n; i++)
{
scanf("%d", &a[i]);
}
int ans = LIS(a, n, lis);
printf("%d\n", ans);
for (i = 0; i < ans; i++)
{
printf("%d ", lis[i]);
}
printf("\n");
}
return 0;
}
O(nlogn)方法求解LIS串,在mask[]中用0标记出来,代码是当时做题的时候写的,没有优化,仅供参考。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = int(2e5 + 5);
const int MAX = 1 << 30;
// 求LIS的长度O(nlogn), LIS元素标记为1
int g_d[N];
int g_pre[N];
typedef struct _Node
{
int v, idx;
} Node;
Node g_stk[N];
int g_top;
bool cmp(const Node &a, const Node &b)
{
return a.v < b.v;
}
int LIS(const int arr[], const int &len, int mask[])
{
g_top = -1;
Node temp;
int i, j, ans = 0, maxId = 0;
g_d[0] = 1;
ans = g_d[0];
maxId = 0;
g_stk[++g_top].v = arr[0];
g_stk[g_top].idx = 0;
for (i = 1; i < len; i++)
{
if (arr[i] > g_stk[g_top].v)
{
g_stk[++g_top].v = arr[i];
g_stk[g_top].idx = i;
j = g_top;
}
else
{
temp.v = arr[i];
temp.idx = i;
j = lower_bound(g_stk, g_stk + g_top + 1, temp, cmp) - g_stk;
g_stk[j] = temp;
}
if (j - 1 >= 0)
{
j = g_stk[j - 1].idx;
g_d[i] = g_d[j] + 1;
g_pre[i] = j;
}
else
{
g_d[i] = 1;
}
if (ans < g_d[i])
{
ans = g_d[i];
maxId = i;
}
// for (j = 0; j <= g_top; j++)
// {
// printf("-%d", g_stk[j]);
// }
// printf("\n");
// for (j = 0; j <= i; j++)
// {
// printf("(d[%d]=%d),", j, g_d[j]);
// }
// printf("\n");
}
j = maxId;
for (i = ans - 1; i >= 0; i--)
{
mask[j] = 1;
j = g_pre[j];
}
return ans;
}
int main()
{
int n, i;
int a[N], mask[N];
while (scanf("%d", &n) != EOF)
{
memset(mask, 0, sizeof(mask));
for (i = 0; i < n; i++)
{
scanf("%d", &a[i]);
}
int ans = LIS(a, n, mask);
// printf("%d\n", ans);
// for (i = 0; i < n; i++)
// {
// printf("%d", mask[i]);
// }
// printf("\n");
int maxv = MAX;
for (i = 0; i < n; i++)
{
if (!mask[i])
{
if (a[i] < maxv) maxv = a[i];
else break;
}
}
if (i < n) printf("NO\n");
else
{
printf("YES\n");
for (i = 0; i < n; i++)
{
printf("%d ", !mask[i]);
}
printf("\n");
}
}
// printf("Time used %.2lf ms\n", 1000 * (double)clock() / CLOCKS_PER_SEC);
return 0;
}