POJ 1836: Alignment

题目在此数组

解题思路:这题出得有点偏离实际,可是呢,编故事原本就是为了丰富题目的,无论它了。.net

这题的本质是:给定一个序列,去掉一些项使其成为先递增后递减的子序列,求去掉项的最小数目。code

初看起来好像有点复杂,但问题每每是换个角度想就一下变简单了。去项很差求,就求保留项好了。如此,这个问题就转变成了求给定序列的先递增后递减子序列的最大长度,去项的最小数目 = 序列长度 N - 符合要求序列的最大长度。blog

具体步骤:将原序列 [0...N) 从 i (1 <= i <= n - 2) 处一分为二,求 [0...i](递增)与 (i...N)(递减)子序列长度的最大和,此即为符合要求的最长序列。至于最长递增/递减子序列的求法,固然用动规了(参见 POJ 2533: Longest Ordered Subsequence)。get

代码:io

#include <cstdio>

const int MAX = 1001;
float a[MAX];
int asc[MAX], des[MAX];

void solve(int n) {
    int ans = 0;
	// 计算最长递增子序列状态数组
    for (int i = 0; i < n; ++i) {
        asc[i] = 1;
        for (int j = i - 1; j >= 0; --j) {
            if (a[i] > a[j] && asc[j] + 1 > asc[i]) {
                asc[i] = asc[j] + 1;
            }
        }
    }

	// 计算最长递减子序列状态数组
    for (int i = n - 1; i >= 0; --i) {
        des[i] = 1;
        for (int j = i + 1; j < n; ++j) {
            if (a[i] > a[j] && des[j] + 1 > des[i]) {
                des[i] = des[j] + 1;
            }
        }
    }

	// 从 i 处分开,[0...i] 的最长递增序列长度 + (i...N) 最长
	// 递减序列长度 = 最长先递增后递减序列的长度
    for (int i = 0; i < n - 1; ++i) {
        for (int j = i + 1; j < n; ++j) {
            if (asc[i] + des[j] > ans) {
                ans = asc[i] + des[j];
            }
        }
    }

	// 用 N 减去最长序列长度即为要去掉的最小项数
    printf("%d\n", n - ans);
}

int main() {
    int n;
    while (scanf("%d", &n) != EOF) {
        for (int i = 0; i < n; ++i) {
            scanf("%f", &a[i]);
        }

        solve(n);
    }

    return 0;
}
相关文章
相关标签/搜索