P1091合唱队列(加最长上升子序列LIS总结)

题目传送,嗖~~~

先总结一下最长上升子序列。

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;
}

                                                                                                                                                                                                                                                                                                                                                                                                                              

猜你喜欢

转载自blog.csdn.net/fighting_yifeng/article/details/81514901