最長遞增子序列
300. 最長遞增子序列
#include <vector>
using namespace std;
class Solution {
public:
// 時間複雜度 O(n^2)
int lengthOfLIS(vector<int> &nums) {
int n = nums.size();
// dp[i]: 以 nums[i] 結尾的最長遞增子序列
vector<int> dp(n);
int res = 0;
for (int i = 0; i < n; ++i) {
dp[i] = 1;
for (int j = 0; j < i; ++j) {
if (nums[j] < nums[i])
dp[i] = max(dp[i], dp[j] + 1);
}
res = max(res, dp[i]);
}
return res;
}
};
#include <vector>
using namespace std;
class Solution {
public:
// 大於等於 target 的左邊界
int binarySearch(vector<int> &ends, int len, int target) {
int left = 0;
int right = len - 1;
int mid;
while (left <= right) {
mid = left + ((right - left) >> 1);
if (ends[mid] >= target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return left;
}
// 時間複雜度 O(n * logn)
int lengthOfLIS(vector<int> &nums) {
int n = nums.size();
// ends[i] 表示所有長度為 i + 1 的遞增子序列的最小結尾
// [0, len-1] 是有效區,有效區內的數字一定嚴格升序
vector<int> ends(n);
// len 表示 ends 陣列目前的有效區長度
int len = 0;
for (int i = 0, pos; i < n; ++i) {
pos = binarySearch(ends, len, nums[i]);
if (pos == len) {
// 找不到就擴充 ends
ends[len++] = nums[i];
} else {
// 找到了就更新成更小的 nums[i]
ends[pos] = nums[i];
}
}
return len;
}
};
#include <vector>
using namespace std;
class Solution {
public:
// 大於 target 的左邊界
int binarySearch(vector<int> &ends, int len, int target) {
int left = 0;
int right = len - 1;
int mid;
while (left <= right) {
mid = left + ((right - left) >> 1);
if (ends[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return left;
}
// 時間複雜度 O(n * logn)
int lengthOfLIS(vector<int> &nums) {
int n = nums.size();
// ends[i] 表示所有長度為 i + 1 的不下降子序列的最小結尾
// [0, len-1] 是有效區,有效區內的數字非遞減
vector<int> ends(n);
// len 表示 ends 陣列目前的有效區長度
int len = 0;
for (int i = 0, pos; i < n; ++i) {
pos = binarySearch(ends, len, nums[i]);
if (pos == len) {
// 找不到就擴充 ends
ends[len++] = nums[i];
} else {
// 找到了就更新成更小的 nums[i]
ends[pos] = nums[i];
}
}
return len;
}
};
354. 俄羅斯套娃信封問題
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
class Solution {
public:
// 找大於等於的左邊界
int binarySearch(vector<int> &ends, int len, int target) {
int left = 0;
int right = len - 1;
int mid;
while (left <= right) {
mid = left + ((right - left) >> 1);
if (ends[mid] >= target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return left;
}
int maxEnvelopes(vector<vector<int>> &envelopes) {
// 寬度從小到大,寬度一樣,高度從大到小
sort(begin(envelopes), end(envelopes),
[](vector<int> &v1, vector<int> &v2) {
return v1[0] == v2[0] ? v1[1] > v2[1] : v1[0] < v2[0];
});
int n = envelopes.size();
// ends[i] 表示長度為 i + 1 的子序列的最小末尾元素
// 在有效區內嚴格遞增
vector<int> ends(n);
// ends 陣列的有效長度
int len = 0;
for (int i = 0, pos; i < n; ++i) {
int target = envelopes[i][1];
pos = binarySearch(ends, len, target);
if (pos == len) {
// 找不到就擴充 ends
ends[len++] = target;
} else {
// 找到了就更新成更小的 nums[i]
ends[pos] = target;
}
}
return len;
}
};
2111. 使陣列 K 遞增的最少操作次數
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
class Solution {
public:
// 大於 target 的左邊界
int binarySearch(vector<int> &ends, int len, int target) {
int left = 0;
int right = len - 1;
int mid;
while (left <= right) {
mid = left + ((right - left) >> 1);
if (ends[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return left;
}
// 時間複雜度 O(n * logn)
int lengthOfLIS(vector<int> &nums) {
int n = nums.size();
// ends[i] 表示所有長度為 i + 1 的不下降子序列的最小結尾
// [0, len-1] 是有效區,有效區內的數字非遞減
vector<int> ends(n);
// len 表示 ends 陣列目前的有效區長度
int len = 0;
for (int i = 0, pos; i < n; ++i) {
pos = binarySearch(ends, len, nums[i]);
if (pos == len) {
// 找不到就擴充 ends
ends[len++] = nums[i];
} else {
// 找到了就更新成更小的 nums[i]
ends[pos] = nums[i];
}
}
return len;
}
int kIncreasing(vector<int> &arr, int k) {
int n = arr.size();
int res = 0;
// 分為 k 組
for (int i = 0; i < k; ++i) {
vector<int> temp;
for (int j = i; j < n; j += k)
temp.emplace_back(arr[j]);
// 累加這一組需要修改的數字
res += temp.size() - lengthOfLIS(temp);
}
return res;
}
};
646. 最長數對鏈
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
int binarySearch(vector<int> &ends, int len, int target) {
int left = 0;
int right = len - 1;
int mid;
while (left <= right) {
mid = left + ((right - left) >> 1);
if (ends[mid] >= target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return left;
}
int findLongestChain(vector<vector<int>> &pairs) {
// 按照數對中第一個數增序
sort(begin(pairs), end(pairs),
[](vector<int> &v1, vector<int> &v2) {
return v1[0] < v2[0];
});
int n = pairs.size();
vector<int> ends(n);
int len = 0;
for (int i = 0, pos; i < n; ++i) {
// 根據數對中第一個數字查
pos = binarySearch(ends, len, pairs[i][0]);
if (pos == len) {
// 插入的是數對中的第二個數字
ends[len++] = pairs[i][1];
} else {
// 改成較小的
ends[pos] = min(ends[pos], pairs[i][1]);
}
}
return len;
}
};
P8776 [藍橋杯 2022 省 A] 最長不下降子序列
#include <vector>
#include <algorithm>
#include <iostream>
using namespace std;
int n, k;
// 求最長不上升子序列長度的二分
// ends[0, len - 1] 為降序,找小於 target 的最左位置
int binarySearch1(vector<int> &ends, int len, int target) {
int left = 0;
int right = len - 1;
int mid;
while (left <= right) {
mid = left + ((right - left) >> 1);
if (ends[mid] < target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return left;
}
// 求最長不下降子序列長度的二分
// ends[0, len-1] 為升序,找大於 target 的最左位置
int binarySearch2(vector<int> &ends, int len, int target) {
int left = 0;
int right = len - 1;
int mid;
while (left <= right) {
mid = left + ((right - left) >> 1);
if (ends[mid] > target) {
right = mid - 1;
} else {
left = mid + 1;
}
}
return left;
}
// 生成輔助陣列 rightMaxLen
// rightMaxLen[i]: 以 nums[i] 開頭的最長不下降子序列長度
// 等價於從右往左遍歷,以 nums[i] 做結尾的情況下的最長不上升子序列
vector<int> getRightMaxLen(vector<int> &ends, vector<int> &nums) {
vector<int> rightMaxLen(nums.size());
int len = 0;
for (int i = n - 1, pos; i >= 0; i--) {
pos = binarySearch1(ends, len, nums[i]);
if (pos == len) {
// 擴充 endsArr
ends[len++] = nums[i];
// 記錄長度
rightMaxLen[i] = len;
} else {
ends[pos] = nums[i];
rightMaxLen[i] = pos + 1;
}
}
return rightMaxLen;
}
int main() {
cin >> n >> k;
vector<int> nums;
nums.resize(n);
for (int i = 0; i < n; ++i)
cin >> nums[i];
// 生成輔助陣列
vector<int> ends(n);
vector<int> rightMaxLen = getRightMaxLen(ends, nums);
int len = 0;
int res = 0;
for (int i = 0, j = k, pos; j < n; i++, j++) {
// 根據當前劃分點查,劃分點左側連續 k 個位置是要改成 nums[j] 的
pos = binarySearch2(ends, len, nums[j]);
// res 由三部分組成
// 左側:劃分點左側連續 k 個位置再往前的區域中,長度為 pos 的不下降子序列(最大值小於 nums[j])
// 中間:劃分點左側連續 k 個位置
// 右側:必須以 nums[j] 開始的不下降子序列的長度
res = max(res, pos + k + rightMaxLen[j]);
// 要插入的是 nums[i],所以要再查詢下插入位置
pos = binarySearch2(ends, len, nums[i]);
if (pos == len) {
ends[len++] = nums[i];
} else {
ends[pos] = nums[i];
}
}
// 特例:最後 k 個元素都改成左側不下降子序列的最後一個值
res = max(res, len + k);
cout << res;
}