题目
题解
动态规划(最长上升子序列)。
求一个先递增再递减的子序列的最大长度。
一般思路:枚举中间单调性发生变化的位置,分别计算左侧的最长递增子序列长度和右侧最长递减子序列长度,记录二者之和的最大值。
优化做法:预处理记录下每个位置对应的左侧的最长递增子序列长度和右侧最长递减子序列长度。枚举中间单调性发生变化的位置时只需要进行加法和比较运算即可,所以枚举的时间复杂度为 O ( n ) O(n) O(n),最终算法的复杂度由 O ( n 2 ) O(n^2) O(n2)的最长上升子序列算法决定。
代码
O(n^2)
// http://bailian.openjudge.cn/practice/2995/
#include<bits/stdc++.h>
using namespace std;
const int N = 1100;
int f[N], g[N]; // f[i]表示景点1~i的最长上升子序列的长度;g[i]表示景点i~n的最长上升子序列的长度 // 预处理时间变成n^2
int a[N], n, ans;
int main()
{
cin >> n;
for (int i = 1;i <= n;i ++) cin >> a[i];
for (int i = 1;i <= n;i ++) {
f[i] = 1;
for (int j = 1;j < i;j ++)
if (a[i] > a[j])
f[i] = max (f[i], f[j] + 1);
int t = n - i + 1;
g[t] = 1;
for (int j = n;j > t;j --) {
if (a[t] > a[j])
g[t] = max (g[t], g[j] + 1);
}
}
for (int i = 1;i <= n;i ++)
ans = max (ans, f[i] + g[i] - 1);
cout << ans << endl;
return 0;
}
O(n^3)
// http://bailian.openjudge.cn/practice/2995/
#include<bits/stdc++.h>
using namespace std;
const int N = 1100; // !
int f[N], a[N];
int main()
{
int n;
cin >> n;
for (int i = 1;i <= n;i ++) cin >> a[i];
int ans = 0; // !
for (int k = 1;k <= n;k ++) {
int ans1 = 0, ans2 = 0; // !
for (int i = 1;i <= k;i ++) {
f[i] = 1;
for (int j = 1;j < i;j ++)
if (a[i] > a[j])
f[i] = max (f[i], f[j] + 1); // !
ans1 = max (ans1, f[i]); // !
}
for (int i = k+1;i <= n;i ++) { // !
f[i] = 1;
for (int j = k+1;j < i;j ++)
if (a[i] < a[j])
f[i] = max (f[i], f[j] + 1); // !
ans2 = max (ans2, f[i]); // !
}
// cout << k << endl;
// for (int i = 1;i <= n;i ++) cout << f[i] << ' ';
// cout << endl << ans1 + ans2 << endl;
ans = max (ans, ans1 + ans2);
}
cout << ans << endl;
return 0;
}
// 叹号标注地方为与“合唱队形”题目不同的地方,但两道题本质都是类似的,细节地方处理不同而已。