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;
}
相關文章
相關標籤/搜索