一般在做这种题目会有两种算法,一个是复杂度是O(n ^ 2),另一种就是O(nlogn)。
O(n ^ 2)算法:
d(i)为以i结尾的最长上升子序列的长度,则d(i) = max{0, d(j) | j < i, Ai > Aj} + 1;最后答案为max{d(i)};
//最长上升子序列
//O(n * n)
int LIS() {
int Max = 1;
memset(dp, 0, sizeof(dp));
for (int i = 0; i < n; i++) {
dp[i] = 1;
for (int j = 0; j < i; j++)
if (a[i] > a[j])
dp[i] = max(dp[i], dp[j] + 1);
if (Max < dp[i])
Max = dp[i];
}
return Max;
}
O(nlogn)算法:
假设已经计算出来的两个状态a和b满足Aa < Ab且d(a) = d(b),则对于后续所有状态i(即i > a 且 i > b)来说,a一定不会比b差——因为如果b所能满足的Ab < Ai的条件,a也能满足且,d(a) = d(b);但相反的a所能满足的b不一定能够满足。所以我们一直保留更小的a时,就一定能保证最后的值是最优解。
而要找到当d值相同但更小的那个数,那就要用到二分查找,来降低时间复杂度了。
//O(N*log(N))
//二分搜索降低时间复杂度(手写二分查找)
int search(int n, int num) {
int l = 1, r = n;
while (l <= r) {
int mid = (l + r) / 2;
if (num > c[mid] && num <= c[mid + 1])
return mid + 1;
else if (num <= c[mid])
r = mid - 1;
else
l = mid + 1;
}
}
void LIS() {
int size = 1, temp, Max = 0;
c[1] = a[1];
dp[1] = 1;
for (int i = 2; i <= n; i++) {
if (a[i] <= c[1])
temp = 1;
else if (a[i] > c[size])
temp = ++size;
else
temp = search(size, a[i]);
c[temp] = a[i];
dp[i] = temp;
if (dp[i] > Max)
Max = d[i];
}
}
//O(N*log(N))
//二分搜索降低时间复杂度(函数二分查找)
void LIS() {
for (int i = 1; i <= n; i++)
g[i] = INF;
for (int i = 0; i < n; i++) {
int k = lower_bound(g + 1, g + n + 1, arr[i]) - g;
d[i] = k;
g[k] = arr[i];
ans = max(ans, d[i]);
}
}