二分dp
二分dp
目前仅发现求解数组最长递增子序列可以用这个,dp[i]表示子序列长度为i+1时结尾的数字。
例题,求数组中最长的严格递增子序列。题目传送门
二分DP进阶版(困难题)
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
vector<int>dp; //dp[i]表示长度为i的递增子序列结尾的数字
dp.push_back(INT_MAX);
for(auto num : nums){
int left = 0,right = dp.size()-1;
while(left<=right){
int mid = (left+right)/2;
if(num<=dp[mid]){
right = mid-1;
}else{
left = mid+1;
}
}
if(left == dp.size()){
dp.push_back(num);
}else{
dp[left] = num;
}
//cout<<num<<" "<<left<<" "<<dp.size()<<endl;
}
return dp.size();
}
};
解题思路:
每次二分实际上是找出dp数组中比num大于等于的数中最小的那个数的下标,那么自动 return left。
这个二分在c++自带函数库中对应为lower_bound,即第一个大于目标值的下标,返回的是迭代器,实现代码如下:
class Solution {
public:
int lengthOfLIS(vector<int>& nums) {
vector<int>dp; //dp[i]表示长度为i的递增子序列结尾的数字
for(auto num : nums){
int left = 0,right = dp.size()-1;
left = lower_bound(dp.begin(),dp.end(),num)-dp.begin();
if(left == dp.size()){
dp.push_back(num);
}else{
dp[left] = num;
}
//cout<<num<<" "<<left<<" "<<dp.size()<<endl;
}
return dp.size();
}
};
#进阶困难版
困难题
这里是引用
小tip: 每一次操作使数组中的一个数字改变为任意数,那么是一个数组变为严格递增即为数组长度减去最大递增子数组的长度。
class Solution {
public:
template<typename T>
int lis(vector<T> a) {
vector<T> dp;
for(auto& x : a) {
auto it = upper_bound(dp.begin(), dp.end(), x);
//因为只要求最长不下降子序列,所以用upper求得大于的那个,然后更改
// 最长递增子序列用lower
if(it == dp.end()) dp.push_back(x); //如果找不打比x大的元素,那么将其加进去
else *it = x; //找到的话,对其进行修改
}
return dp.size();
}
int kIncreasing(vector<int>& a, int k) {
int n = a.size();
int ans = 0;
for(int i = 0; i < k; ++i) {
vector<int> b;
for(int j = i; j < n; j += k) {
b.push_back(a[j]);
}
ans += lis(b);
}
return n - ans;
}
};
作者:elegant-kapitsaexi
链接:https://leetcode-cn.com/problems/minimum-operations-to-make-the-array-k-increasing/solution/fen-zu-lis-by-elegant-kapitsaexi-prmn/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。