解题思路:这题出得有点偏离实际,但是呢,编故事本来就是为了丰富题目的,不管它了。
这题的本质是:给定一个序列,去掉一些项使其成为先递增后递减的子序列,求去掉项的最小数目。
初看起来好像有点复杂,但问题往往是换个角度想就一下变简单了。去项不好求,就求保留项好了。如此,这个问题就转变成了求给定序列的先递增后递减子序列的最大长度,去项的最小数目 = 序列长度 N - 符合要求序列的最大长度。
具体步骤:将原序列 [0...N) 从 i (1 <= i <= n - 2) 处一分为二,求 [0...i](递增)与 (i...N)(递减)子序列长度的最大和,此即为符合要求的最长序列。至于最长递增/递减子序列的求法,当然用动规了(参见 )。
代码:
#includeconst 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;}