LeetCode | Maximum Product Subarray

来源:互联网 发布:数据化人生 好看吗 编辑:程序博客网 时间:2024/06/14 07:49

Find the contiguous subarray within an array (containing at least one number) which has the largest product.

For example, given the array [2,3,-2,4],
the contiguous subarray [2,3] has the largest product = 6.

这道题应当算是第一个独立做出来的DP了~撒花


经历了MLE、TLE和各种WA,终于找到了几乎是最优的解答方式(和Gas Station有点类似)

首先考虑这是一个DP问题。
因为首先,一个区间的最大值可以归结为其子区间的最大值问题,当前做了决策之后对后续不会产生影响(后续可以利用当前计算的结果)


一开始想是dp[i][j]与dp[i+1][j-1]之间的关系,随后想到如何处理负数的问题。
于是开启两个dp数组,一个存最大一个存最小,判断最大的时候也需要判断最小*当前值【因为有可能出现负的】
于是一开始写出的类似
dp1[i][j]=max{dp1[i+1][j-]*nums[j],dp2[i+1][j-1]}
随后问题来了,为何元素的扩展状态是2个方向??
之前回文问题可以这么做是因为回文要求两端相等,而这个问题则需要仔细考虑。
于是写出:
利用dp[i][j]记录从i~j位置乘积的最大值(正数)

dp[i][j]=max{dp[i][j-1]*nums[j],nums[j]}

意思也就是,第j位的元素等于前i~j-1位乘积最大值乘以当前值,如果比当前值大,就更新dp[i][j],否则就继续从j开始计算,dp[i][j]更新为nums[j]

如果遇到负数,那么这个数在将来有可能成为最大的数,所以负数需要额外开辟一个dp数组,专门用于存储最小值,这些最小值(负数)是“有潜力”成为最大值的数。
所以dp方程相应变为


dp[i][j]=max{dp[i][j-1]*nums[j],nums[j],dp2[i][j-1]*nums[j]}
dp2[i][j]=min{dp[i][j-1]*nums[j],nums[j],dp2[i][j-1]*nums[j]}


到这里终于正确了,不过一运行MLE。。。说明空间申请了太多了,看用例是给了一个大数组,这样n*n肯定是要爆掉的。

于是考虑可能并不需要数组就可以记录?

可以看到dp方程里面,i的值始终未变化,而产生变化的只有j。这说明dp[i][j]并不依赖于i点的状态,而是依赖于j-1点的状态。
所以直接考虑的是每次遍历的时候拿一个maxx和minn分别记录从i到当前位置的最大值。
因为原循环中也仅仅是利用了历史状态,记忆化搜索而已,但这些搜索的结果我们并不是完全都要,只要记录之前记录到的最大值并对它进行更新就好了

// for(int i=0;i<n;i++){  //     int maxx=0,minn=0;  //     for(int j=i;j<n;j++){            int maxv=maxx,minv=minn;            maxx=maxNum(maxv*nums[j],nums[j],minv*nums[j]);            minn=minNum(maxv*nums[j],nums[j],minv*nums[j]);        }}

这样确实直接省了2个数组,时间复杂度还是O(n^2) 但是空间复杂度由O(n^2)降到了O(1);
very nice;

跑一遍,TLE。。。
说明了什么,说明了二重循环也是没有必要的…
仔细想想,在i=0的时候,其实已经计算了从0开始,到n-1的最大值。
那为何还要计算从非第一个起点开始的呢?不还是遍历整个数组吗?

以此为启发,想到删除里层循环,只保留一个maxx和minn以及记录中间结果的val。

一次遍历,即可解决

class Solution {public:    int maxProduct(vector<int>& nums) {        int n=nums.size();        if(n==1) return nums[0];        //选取第i位为结尾的子串作为最大值        // vector<int> dp1(n,0);        // vector<int> dp2(n,0);        // int val=INT_MIN;        // for(int i=0;i<n;i++){        //     dp1[i]=maxNum(dp1[i-1]*nums[i],nums[i],dp2[i-1]*nums[i]);        //     dp2[i]=minNum(dp1[i-1]*nums[i],nums[i],dp2[i-1]*nums[i]);        // }        // for(int i=0;i<n;i++)        // if(dp1[i]>val) val=dp1[i];        //如果是以i开始j结束为标记,需要n*n大小的标记        // vector<vector<int> > dp1;        // vector<vector<int> > dp2;        // dp1.resize(n,vector<int>(n));        // dp2.resize(n,vector<int>(n));        // for(int i=0;i<n;i++){        //     for(int j=0;j<n;j++){        //         dp1[i][j]=dp2[i][j]=0;        //     }        // }        //最优解法        int val=nums[0],maxx=nums[0],minn=nums[0];        for(int j=1;j<n;j++){            int maxv=maxx,minv=minn;            maxx=maxNum(maxv*nums[j],nums[j],minv*nums[j]);            minn=minNum(maxv*nums[j],nums[j],minv*nums[j]);            val=maxx>val?maxx:val;        }        // int val=INT_MIN;        // for(int i=0;i<n;i++){        //     int maxx=0,minn=0;        //     for(int j=i;j<n;j++){        //         //超时        //         int maxv=maxx,minv=minn;        //         maxx=maxNum(maxv*nums[j],nums[j],minv*nums[j]);        //         minn=minNum(maxv*nums[j],nums[j],minv*nums[j]);        //         // 超内存                   // 记录每一行每一列的的dp[i][j]表示以i开头j结尾的子串的乘积        //         // dp1[i][j]=maxNum(dp1[i][j-1]*nums[j],nums[j],dp2[i][j-1]*nums[j]);        //         // dp2[i][j]=minNum(dp1[i][j-1]*nums[j],nums[j],dp2[i][j-1]*nums[j]);        //         val=maxx>val?maxx:val;        //         // if(nums[i] * nums[j]>0){        //         //     //这种模拟最长公共子序列的有可能是不连续的        //         //     dp1[i][j]=dp1[i+1][j-1]*nums[i]*nums[j];        //         //     dp2[i][j]=dp2[i+1][j-1]*nums[i]*nums[j];        //         // }        //         // else if(nums[i] * nums[j]<0){        //         //     dp1[i][j]=max(dp1[i+1][j-1]*nums[i],dp2[i+1][j-1]*nums[j]);        //         //     dp2[i][j]=min(dp1[i+1][j-1]*nums[j],dp2[i+1][j-1]*nums[i]);        //         // }        //         // else if(nums[i]<0 && nums[j]>0){        //         //     dp1[i][j]=max(dp2[i+1][j-1]*nums[i],dp1[i+1][j-1]*nums[j]);        //         //     dp2[i][j]=min(dp2[i+1][j-1]*nums[j],dp1[i+1][j-1]*nums[i]);        //         // }        //         // else if(nums[i]*nums[j]==0){        //         // }        //     }        // }        // output(dp1);        return val;        // return getMax(dp1);    }    int maxNum(int a,int b,int c){        int val=a>b?a:b;        return val>c?val:c;    }    int minNum(int a,int b,int c){        int val=a<b?a:b;        return val<c?val:c;    }    int getMax(vector<vector<int>> &result){        int val=INT_MIN;        for(int i=0;i<result.size();i++){            for(int j=0;j<result[0].size();j++){                if(result[i][j]>val)                val=result[i][j];            }        }        return val;    }    void output(vector<vector<int>> &result){        for(int i=0;i<result.size();i++){            for(int j=0;j<result[i].size();j++){                printf("%d ",result[i][j]);            }            printf("\n");        }    }};
0 0
原创粉丝点击