动态规划之最大连续子序列和子序列积及递增序列

2018/03/13 Algorithms 共 2947 字,约 9 分钟

动态规划之最大连续子序列和

问题描述:

给定一个整数序列,a0, a1, a2, …… , an(项可以为负数),求其中最大的子序列和。如果所有整数都是负数,那么最大子序列和为0;

动态规划解法

这是最常见的方法了,简单有效,好理解。

状态转移方程:sum[i] = max{sum[i-1]+a[i],a[i]}. (sum[i]记录以a[i]为子序列末端的最大序子列连续和)

其实完全可以不用开数组,累计sum直到sum + a < a,把sum赋值为a,更新最大值就行了。

//动态规划算法
int MaxSum(int n)
{
    int sum=0,b=0;
    for(int i=0;i<n;i++)
    {
        if(b>0) b+=a[i];
        else b=a[i];
        if(b>sum) sum=b;
    }
    return sum;
}

Python

def max_sum(n, a):
    sum_val = 0
    b = 0
    for i in range(n):
        if b > 0:
            b += a[i]
        else:
            b = a[i]
        if b > sum_val:
            sum_val = b
    return sum_val

C++: 找最大的对应的左右区间。

#include <iostream>
#include <vector>
using namespace std;
int main() {
    int n;
    scanf("%d", &n);
    vector<int> v(n);
    int leftindex = 0, rightindex = n - 1, sum = -1, temp = 0, tempindex = 0;
    for (int i = 0; i < n; i++) {
        scanf("%d", &v[i]);
        temp = temp + v[i];
        if (temp < 0) {
            temp = 0;
            tempindex = i + 1;
        } else if (temp > sum) {
            sum = temp;
            leftindex = tempindex;
            rightindex = i;
        }
    }
    if (sum < 0) sum = 0;
    printf("%d %d %d", sum, v[leftindex], v[rightindex]);
    return 0;
}

动态规划之最大连续子序列乘积

与之类似

Maximum Product Subarray

class Solution {
public:
    int maxProduct(vector<int>& nums) {
        int a = nums[0], b = nums[0];
        int res = a;
        for (int i = 1; i < nums.size(); i++) {
            int a1 = a;
            a = max(a*nums[i], max(nums[i], b*nums[i]));
            b = min(b*nums[i], min(nums[i], a1*nums[i]));
            res = max(a, res);
            cout << a << " " << b << " " << res << endl;
        }
        return res;
    }
};

动态规划之最长递增子序列

leetcode题目

用f[i]记录位置i的数结尾的序列长度,初始化皆为1

//java:
public class Test {  
    public static void main(String[] args) {  
        int[] num = new int[]{1,4,7,2,5,8,3,6,9};  
        int[] f = new int[num.length];  
        for (int i = 0;i < num.length;i++) {  
            f[i] = 1;  
        }  
        int max = f[0];  
        for (int i = 1;i < num.length;i++) {  
            for (int j = 0;j < i;j++) {  
                //更新条件,1是有序,2是长度能变长  
                if (num[i] >= num[j] && f[i] < f[j] + 1) {  
                    f[i] = f[j] + 1;  
                    if (f[i] > max) {  
                        max = f[i];//更新最长的长度  
                    }  
                }  
            }  
        }  
        System.out.println(max);  
    }  
}  

使用一个辅助空间B数组。 Bi存储Dp值为i的最小的数字。(有多个位置,以这些位置为结尾的LIS长度都为i, 则这些数字中最小的一个存在Bi中) 则B数组严格递增。且下标表示LIS长度,也是严格递增,可以在B数组中进行二分查找。

对于每个位置i,我们要找,所有小于Ai, 且Dp值最大的那个,然后把Dp(i+1)更新为A(i)。这个操作在B数组中二分查找。

class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        d = []
        for n in nums:
            if not d or n > d[-1]:
                d.append(n)
            else:
                l, r = 0, len(d) - 1
                loc = r
                while l <= r:
                    mid = (l + r) // 2
                    if d[mid] >= n:
                        loc = mid
                        r = mid - 1
                    else:
                        l = mid + 1
                d[loc] = n
        return len(d)

动态规划之最长非递减子序列

最长非递减子序列。

差异在于递增还是非递减。



# 最长非递减子序列
def LIS(self, nums: List[int]):
    g = []
    for x in nums:
        import bisect
        j = bisect.bisect_right(g, x)
        if j == len(g):
            g.append(x)
        else:
            g[j] = x
    return len(nums) - len(g)
  

最大非递减子序列和

O(nlogn)

线段树维护前面的最大子序列和。每次找小于当前元素的最大子序列和。

MOD = int(1e9 + 7)
INF = float('inf')
import sortedcontainers
import bisect
import heapq

class Solution:
    def maxBalancedSubsequenceSum(self, nums: List[int]) -> int:
        # len = 12
        d = []
        n = len(nums)
        l = INF
        r = float('-inf')
        for i in range(n):
            if nums[i] > 0:
                # l = min(l, nums[i] - i)
                # r = max(r, nums[i] - i)
                d.append((nums[i] - i, nums[i]))
        if not d:
            return max(nums)
        seg = SegmentTree(SegOpCollect.Collect_Max, SegOpUpdate.Update_SetVal, default_val=0)
        diff2index = {}
        x = sorted(set([v[0] for v in d]))
        # 离散化
        for v in x:
            diff2index[v] = len(diff2index)
        # print(d)
        # print(diff2index)
        l, r = 0, len(diff2index)
        # len = 12
        seg.build(left=l, right=r)
        for v in d:
            t = seg.query_interval(l, diff2index[v[0]])
            seg.set_point(diff2index[v[0]], t + v[1])
        return seg.query_interval(l, r)

Search

    Table of Contents