北大考研复试上机——合唱队形

N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学不交换位置就能排成合唱队形。合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1, 2, …, K,他们的身高分别为T1, T2, …, TK,则他们的身高满足T1 < T2 < … < Ti , Ti > Ti+1 > … > TK (1 <= i <= K)。你的任务是,已知所有N位同学的身高,计算最少需要几位同学出列,可以使得剩下的同学排成合唱队形。

输入描述:

输入的第一行是一个整数N(2 <= N <= 100),表示同学的总数。
第一行有n个整数,用空格分隔,第i个整数Ti(130 <= Ti <= 230)是第i位同学的身高(厘米)。

输出描述:

可能包括多组测试数据,对于每组数据,
输出包括一行,这一行只包含一个整数,就是最少需要几位同学出列。
示例1

输入

8
186 186 150 200 160 130 197 220

输出

4

    思路:这是一个动态规划里最长上升(下降)子序列的问题,只不过分成两部分。大致过程就是对于每个学生k,计算以其为中心的队形的最大长度,也就是说,①0~k这k个学生的某个子序列高度是升序的且一定是包括k本人的;②k~N这N-k+1个学生的某个子序列的高度是降序的,同样包括k本人。

    这样就把这个问题分成了两个计算最长上升(下降)子序列的问题了,这两个子序列的长度之和减一(因为要减去一个重复的k本人)就是以k为中心的最长队形,计算出以每个人为中心的最长队形,其最大值就是题目要求的数字了。

    以最长0~k的最长上升子序列为例,递推公式如下:

              dp[k] = 1;

              dp[i] = max{ dp[j] } + 1;  (0 <= i < k && height[i] < height[j] && i < j <= k)

    dp[i]表示从i到k的最长上升子序列,一定包括i和k这两个人,j是i之后的某个人,如果j到k有一个子序列且i的高度小于j的高度,那在这个序列前方加上i仍旧是一个子序列,序列长度加一。而如果j到k没有子序列,那i到k之间也就没有包含j的队列。最终算出的dp[l]的最大值(0 <= l <= k)就是以k为末尾的最长的上升子序列了。

#include 
#include 
using namespace std;

int child_left(int *height, int n)
{
    if(n == 1) return 1;
    int dp[n];
    memset(dp, 0, sizeof(dp));
    dp[n-1] = 1;
    for(int i = n-2; i >= 0; i--)
    {
        int maxn = -1;
        for(int j = i+1; j <= n-1; j++)
        {
            if(height[i] < height[j] && dp[j] != -1)
                maxn = max(maxn, dp[j] + 1);
        }
        dp[i] = maxn;
    }
    int retVal = -1;
    for(int i = 0; i < n; i++)
        retVal = max(retVal, dp[i]);
    return retVal;
}

int child_right(int *height, int n)
{
    if(n == 1) return 1;
    int dp[n];
    memset(dp, 0, sizeof(dp));
    dp[0] = 1;
    for(int i = 1; i <= n-1; i++)
    {
        int maxn = -1;
        for(int j = 0; j < i; j++)
        {
            if(height[i] < height[j] && dp[j] != -1)
                maxn = max(maxn, dp[j] + 1);
        }
        dp[i] = maxn;
    }
    int retVal = -1;
    for(int i = 0; i < n; i++)
        retVal = max(retVal, dp[i]);
    return retVal;
}

int cal(int *height, int n, int index)
{
    if(index > 0 && index + 1 < n)
        return child_left(height, index + 1) + child_right(height + index, n - index) - 1;
    else if(index == 0)
        return child_right(height, n);
    else return child_left(height, n);
}

int main()
{
    int n;
    while(cin >> n)
    {
        int height[n], maxlen = -1;
        for(int i = 0; i < n; i++)
        {
            cin >> height[i];
        }

        for(int i = 0; i < n; i++)
        {
            maxlen = max(cal(height, n, i), maxlen);
        }
        cout << n - maxlen << endl;
    }
}


你可能感兴趣的:(c/c++)