先总结一下最长上升子序列。
1.什么是最长上升子序列 给出一组数,求取按递增顺序的最长的一组数,即最长上升序列。
2.两种解题思路:
1》dp[i] = 以a[i]结尾的最长上升子序列长度
递推: 以a[i]结尾的最长上升序列有两种 :1.只包含a[i]2.j<i&&a[j]<a[i]将a[i]加到以a[j]结尾的最长上升序列后。
状态转移方程:if(a[j]<a[i]) dp[i] = max(dp[i],a[j]+1)
2》如果子序列长度相同,则末尾数越小越容易接更多元素(即可能变得更长)即如果数大于末尾元素就把他加在后面,如果小于末尾元素则找到第一个不大于他的用它来更新数组。二分查找,可使复杂度降低。
为啥可行呢?你可能会有这样的疑问,为啥能拿不是按一定顺序的数来更新数组呢,不会出岔子吗?
不会的,你可以想一想真正使数组增长的是这个数大于最后一个元素,而能使最后一个元素更新的一定是在他后面的数,因而不会有你所想的顺序混乱的存在。
合唱队列的AC代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
using namespace std;
const int maxn = 110;
int a[maxn];
int dp[maxn],ap[maxn];
//最长升,最长降(正向反向最长上升子序列)
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
memset(dp, 0, sizeof(dp));
for (int i = 0; i < n; i++)
{
dp[i] = 1;
for (int j = 0; j < i; j++)
{
if(a[j]<a[i])
dp[i] = max(dp[i], dp[j] + 1);
}
}
for (int i = n-1; i >=0; i--)
{
ap[i] = 1;
for (int j = n-1; j > i; j--)
{
if(a[j]<a[i])
ap[i] = max(ap[i], ap[j] + 1);
}
}
int res = 0;
for (int i = 0; i < n; i++)
{
res = max(res, ap[i] + dp[i]-1);
}
cout << n-res << endl;
return 0;
}