825 DP(kadane算法) lc.53 lc.918

lc.53 最大子序和

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。
kadane算法 >>求最大和 可延伸最小和等变种

  • DP
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int target=nums[0];
        int sum = 0;

        for(int i=0;i<nums.size();i++)
        {
            if(sum>0)
            {
                sum += nums[i];
            }
            else
            {
                sum = nums[i];
            }

            target = max(target,sum);
        }

        return target;

    }
};

一个月前做过,又给整不会了,mad

  • DP 状态转移方程
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int target=nums[0];
        int sum = 0;

        for(int i=0;i<nums.size();i++)
        {
            sum=nums[i]+max(sum,0);
            target = max(target,sum);
        }

        return target;

    }
};

kadane的状态转移方程

  • 线段树
class Solution {
public:
    struct Status {
        int lSum, rSum, mSum, iSum;
    };

    Status pushUp(Status l, Status r) {
        int iSum = l.iSum + r.iSum;
        int lSum = max(l.lSum, l.iSum + r.lSum);
        int rSum = max(r.rSum, r.iSum + l.rSum);
        int mSum = max(max(l.mSum, r.mSum), l.rSum + r.lSum);//max括号里只能有两个值,多个需要嵌套
        return (Status) {lSum, rSum, mSum, iSum};
    };

    Status get(vector<int> &a, int l, int r) {
        if (l == r) {
            return (Status) {a[l], a[l], a[l], a[l]};
        }
        int m = (l + r) >> 1;
        Status lSub = get(a, l, m);
        Status rSub = get(a, m + 1, r);
        return pushUp(lSub, rSub);
    }

    int maxSubArray(vector<int>& nums) {
        return get(nums, 0, nums.size() - 1).mSum;
    }
};

lc.918 环形子数组的最大和

给定一个由整数数组 A 表示的环形数组 C,求 C 的非空子数组的最大可能和。
在此处,环形数组意味着数组的末端将会与开头相连呈环状。(形式上,当0 <= i < A.length 时 C[i] = A[i],且当 i >= 0 时 C[i+A.length] = C[i])
此外,子数组最多只能包含固定缓冲区 A 中的每个元素一次。(形式上,对于子数组 C[i], C[i+1], …, C[j],不存在 i <= k1, k2 <= j 其中 k1 % A.length = k2 % A.length)

  • 超时
class Solution {
public:
    int maxSubarraySumCircular(vector<int>& nums) {
        int c[2*nums.size()];

        for(int i=0;i<nums.size();i++)
        {
            c[i]=nums[i];
            c[nums.size()+i]=nums[i];
        }

        int sum=0;
        int target=c[0];

        for(int j=0;j<nums.size();j++){
        for(int i=j;i<j+nums.size();i++)
        {
            if(sum<0)
            {
                sum=c[i];
            }
            else
            {
                sum += c[i];
            }

            target=max(target,sum);
        }
        sum=0;
        }

        return target;

    }
};

直接在上一问基础上改动,n^2超时,意料之中,情理之中

  • 邻接数组

单区间情况还是kadane解决,双区间选择分别计算,左侧到i结束,求和;右侧从i+2开始,查找最大和,由于要包含最后一个数,所以反向查找。
Java实现

class Solution {
    public int maxSubarraySumCircular(int[] A) {
        int N = A.length;

        int ans = A[0], cur = A[0];
        for (int i = 1; i < N; ++i) {
            cur = A[i] + Math.max(cur, 0);
            ans = Math.max(ans, cur);
        }//中间情况,kadane

        // ans is the answer for 1-interval subarrays.
        // Now, let's consider all 2-interval subarrays.
        // For each i, we want to know
        // the maximum of sum(A[j:]) with j >= i+2

        // rightsums[i] = A[i] + A[i+1] + ... + A[N-1]
        int[] rightsums = new int[N];
        rightsums[N-1] = A[N-1];
        for (int i = N-2; i >= 0; --i)
            rightsums[i] = rightsums[i+1] + A[i];

        // maxright[i] = max_{j >= i} rightsums[j]
        int[] maxright = new int[N];
        maxright[N-1] = A[N-1];
        for (int i = N-2; i >= 0; --i)
            maxright[i] = Math.max(maxright[i+1], rightsums[i]);

        int leftsum = 0;
        for (int i = 0; i < N-2; ++i) {
            leftsum += A[i];
            ans = Math.max(ans, leftsum + maxright[i+2]);
        }

        return ans;
    }
}

一开始不清楚为什么左侧要求和,实际上相当于从数列第一项开始依次作为新数列末尾,剩余部分从尾部开始查找最大和。

  • 前缀和 + 单调队列

和我一开始想的一样,两个nums拼成一个定长数列解决。
在这里插入图片描述Java实现,细节繁琐,实在牛批

class Solution {
    public int maxSubarraySumCircular(int[] A) {
        int N = A.length;

        // Compute P[j] = B[0] + B[1] + ... + B[j-1]
        // for fixed array B = A+A
        int[] P = new int[2*N+1];
        for (int i = 0; i < 2*N; ++i)
            P[i+1] = P[i] + A[i % N];

        // Want largest P[j] - P[i] with 1 <= j-i <= N
        // For each j, want smallest P[i] with i >= j-N
        int ans = A[0];
        // deque: i's, increasing by P[i]
        Deque<Integer> deque = new ArrayDeque();
        deque.offer(0);

        for (int j = 1; j <= 2*N; ++j) {
            // If the smallest i is too small, remove it.
            if (deque.peekFirst() < j-N)
                deque.pollFirst();

            // The optimal i is deque[0], for cand. answer P[j] - P[i].
            ans = Math.max(ans, P[j] - P[deque.peekFirst()]);

            // Remove any i1's with P[i2] <= P[i1].
            while (!deque.isEmpty() && P[j] <= P[deque.peekLast()])
                deque.pollLast();

            deque.offerLast(j);
        }

        return ans;
    }
}

  • 单区间:kadane 双区间:sum - kadane (min变种)

很简便的一种方法,两次kadane。但是注意到,当全员负数时sum-minv==0,最大子序和等于数组中最大的那个 maxv

class Solution {
public:
    int maxSubarraySumCircular(vector<int>& nums) {
        int sum=nums[0];
        vector<int>dpmax(nums);
        vector<int>dpmin(nums);
        for(int i=1;i<nums.size();i++){
            dpmax[i]=max(dpmax[i-1]+nums[i],nums[i]);
            dpmin[i]=min(dpmin[i-1]+nums[i],nums[i]);
            sum+=nums[i];
        }
        int maxv=*max_element(dpmax.begin(),dpmax.end());
        int minv=*min_element(dpmin.begin(),dpmin.end());
        return max(maxv,sum-minv==0?maxv:sum-minv);
    }
};

max_element 与 min_element 都是 algorithm 库里的库函数,分别返回范围内最大与最小值。注意到 *max_element 与 *min_element。

  • 双区间:kadane 符号变种

在这里插入图片描述

Java实现

class Solution {
    public int maxSubarraySumCircular(int[] A) {
        int S = 0;  // S = sum(A)
        for (int x: A)
            S += x;

        int ans1 = kadane(A, 0, A.length-1, 1);
        int ans2 = S + kadane(A, 1, A.length-1, -1);
        int ans3 = S + kadane(A, 0, A.length-2, -1);
        return Math.max(ans1, Math.max(ans2, ans3));
    }

    public int kadane(int[] A, int i, int j, int sign) {
        // The maximum non-empty subarray for array
        // [sign * A[i], sign * A[i+1], ..., sign * A[j]]
        int ans = Integer.MIN_VALUE;
        int cur = Integer.MIN_VALUE;
        for (int k = i; k <= j; ++k) {
            cur = sign * A[k] + Math.max(cur, 0);
            ans = Math.max(ans, cur);
        }
        return ans;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值