PS: 一个新员工和一个老员工价值相当,老员工就可以走了,因为新员工被榨取的剩余空间更多。
PS: 最终二分查找数组的长度是最长递增子序列的长度,但是二分查找数组不是对应的最长递增子序列。
python 代码: 动态规划, 贪心+二分法
from typing import List
class Solution:
class Solution:
def lengthOfLIS0(self, nums: List[int]) -> int: # 动态规划: dp[i] 表示以 nums[i] 这个数结尾的最长递增子序列的长度。
if nums is None or len(nums) == 0:
return 0
res = 1
dp = [1 for _ in range(len(nums) + 1)]
for i in range(len(nums)):
for j in range(i):
if nums[j] < nums[i]:
dp[i] = max(dp[i], dp[j] + 1)
res = max(res, dp[i])
return res
def lengthOfLIS(self, nums: List[int]) -> int: # 贪心+二分查找
if nums is None or len(nums) == 0:
return 0
LIS = [nums[0]]
for a in nums[1:]:
flag = True
l, r = 0, len(LIS) - 1
while l <= r:
if a > LIS[-1]:
LIS.append(a)
flag = False
break
if a <= LIS[0]:
LIS[0] = a
flag = False
break
m = (l + r) // 2
if LIS[m] == a:
flag = False
break
elif LIS[m] > a:
r = m
else:
l = m + 1
if flag:
LIS[l] = a
return len(LIS)
if __name__ == "__main__":
s = Solution()
nums = [10, 9, 2, 5, 3, 7, 101, 18]
res = s.lengthOfLIS(nums)
print("res: ", res)
go 代码:动态规划, 贪心+二分法
package main
import "fmt"
func max(a int, b int) int {
if a > b {
return a
}
return b
}
func lengthOfLIS0(nums []int) int { // 动态规划: dp[i] 表示以 nums[i] 这个数结尾的最长递增子序列的长度。
res := 0
n := len(nums)
dp := make([]int, n+1)
for i := 0; i < n; i++ {
dp[i] = 1
for 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
}
func lengthOfLIS(nums []int) int { //贪心+二分查找
top := []int{nums[0]} //贪心数组,用于记录长度为i的子序列末尾的最小元素值
for i := 1; i < len(nums); i++ {
length := len(top)
if nums[i] > top[length-1] {
top = append(top, nums[i])
} else if nums[i] < top[length-1] { //二分查找 找到top数组中刚好比 nums[i] 大的数, 并用 nums[i] 替换掉 top 数组中 。这样,不影响max数组的长度,同时更有利于nums后续的数值进入 top
var mid int
l, r := 0, length-1
for l < r {
mid = (l + r) / 2
if top[mid] == nums[i] { //遇到top数组已有的值需跳过
break
} else if nums[i] < top[mid] {
r = mid
} else { //小技巧:由于mid向下取整,l取mid+1,最后l就是需要改变的vec数组下标
l = mid + 1
}
}
if top[mid] == nums[i] {
continue
} //遇到top数组已有的值需跳过
top[l] = nums[i]
}
}
return len(top)
}
func main() {
var p = []int{10, 9, 2, 5, 3, 7, 101, 18}
fmt.Println(lengthOfLIS(p)) //求比较次数的数量级是O(1.5n) 更少的比较次数可以 分治 比较
}
c++代码:贪心+二分
#include <iostream>
#include <vector>
using namespace std;
class Solution
{
public:
int lengthOfLIS(vector<int> &nums)
{
int len = 1, n = (int)nums.size();
if (n == 0)
{
return 0;
}
vector<int> d(n + 1, 0);
d[len] = nums[0];
for (int pos, i = 1; i < n; ++i)
{
if (nums[i] > d[len])
{
d[++len] = nums[i];
}
else
{
pos = binary_search(d, len, nums[i]);
d[pos] = nums[i];
}
}
return len;
}
private:
int binary_search(vector<int> d, int len, int value)
{
int L = 1, R = len; // 数组d中第一个大于等于value的数的指针,如果找不到说明所有的数都比 nums[i] 大,此时要更新 d[1],所以这里将 L 设为 0
while (L <= R)
{
int mid = (L + R) >> 1;
if (d[mid] < value)
{
L = mid + 1;
}
else
{
R = mid - 1;
}
}
return L;
}
};
int main()
{
Solution sol = *new Solution();
vector<int> a = {5, 7, 1, 9, 4, 6, 2, 8, 3};
int res = sol.lengthOfLIS(a);
printf("res=%d\n", res);
return 0;
}
C++ 代码 P1020 [NOIP1999 普及组] 导弹拦截
#include <cstring>
#include<iostream>
using namespace std;
int x, arr[100005], tail[100005], len = 0, ans = 1, ans1 = 1;
int binary_search(int from, int to, int value) {
int left = from, right = to;
while (left <= right) {
int mid = left + (right - left) / 2;
if (tail[mid] >= value) {
left = mid + 1;
}
else {
right = mid-1;
}
}
return left;
}
int main() {
while (cin >> x) { arr[++len] = x; } // 389 207 155 300 299 170 158 65 EOF
tail[1] = arr[1];
for (int i = 2; i <= len; i++) {
if (arr[i] <= tail[ans]) {
tail[++ans] = arr[i];
}
else {
int j = binary_search(1, ans, arr[i]);
tail[j] = arr[i];
}
}
printf("%d\n", ans); // 获得最长不上升子序列长度
memset(&tail, 0, sizeof(tail));
tail[1] = arr[1];
for (int i = 1; i <= len; i++) {
if (arr[i] > tail[ans1]) {
tail[++ans1] = arr[i];
}
else {
int j = lower_bound(tail + 1, tail + 1 + ans1, arr[i]) - tail; // lower_bound(b, b+len+1 ,x ,cmp) 返回[b,b+len+1)中第一个大于等于x的数的指针。
tail[j] = arr[i];
}
}
printf("%d", ans1); // 获得最长上升子序列的长度, dilworth定理:一个序列中可以分割最长不升子序列的数量=这个序列中最长上升子序列的长度
return 0;
}
C++ 更精简的代码:
#include<algorithm>
#include<iostream>
using namespace std;
int a[100010];
int b[100010];
int n;
int len = 1;
int main() {
while (cin >> a[n++]); // 389 207 155 300 299 170 158 65 EOF
n--; //有读入终止,eof也会被读进数组里,要n--
b[1] = a[0];
for (int i = 1; i < n; i++) {
if (a[i] <= b[len]) b[++len] = a[i];
else *upper_bound(b + 1, b + len + 1, a[i], greater<int>()) = a[i]; //upper_bound(b, b+len+1, x ,cmp) 返回[b,b+len+1)中第一个大于x的数的指针, 当cmp=greater<int>()时大于变成小于
}
cout << len << endl; //获得最长不上升子序列长度
len = 1;
b[1] = a[0];
for (int i = 1; i < n; i++) {
if (a[i] > b[len]) b[++len] = a[i];
else *lower_bound(b + 1, b + len + 1, a[i]) = a[i]; //lower_bound(b, b+len+1 ,x ,cmp) 返回[b,b+len+1)中第一个大于等于x的数的指针
}
cout << len; // 获得最长上升子序列的长度, dilworth定理:一个序列中可以分割最长不升子序列的数量=这个序列中最长上升子序列的长度
return 0;
}
参考链接: