python & go & C++ LeetCode 300 最长递增子序列 两种解法:动态规划,贪心+二分查找, C++ P1020 [NOIP1999 普及组] 导弹拦截

leetcode 300 题目

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;
}

参考链接:

【1】动态规划设计之最长递增子序列

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值