最大数字序列和问题,买卖股票问题,以及最长公共字串问题
最大数字序列和问题
最大数字序列和原理
给定由n个整数(可能为负整数)组成的序列A1,A2,A3,...,An,求该序列的连续子段的和的最大值。当所有整数均为负整数时定义其最大子段和为0
例如 {-4, 11,-2, 13,-7,-3,12} 的最大子段和为22
int MaxSum(int[] array,int n) { int b=0; int sum=0; for(int i=1;i<=n;i++) { if(b>0) { b+=array[i]; } else { b=array[i]; } if(b>sum) { sum=b; } } return sum; }
买卖股票问题:
点击打开链接
1,一次买卖股票受益最大
分析:此题就是选择买入卖出股票的最大收益,对于第i天卖出的最大收益即为第i天的股市价格减去[0,i-1]天内的最小股市价格,当第i天的股市价格比漆面最低股市价格还低,则更新最低股市价格。然后取最大的股市收益,为DP问题。用profit[i]表示第i天的收益,则minBuyPrice = min(minBuyPrice, prices[i]),并且profit[i] = prices[i]-minBuyPrice. 然后取profit中的最大值。
class Solution { public: int maxProfit(vector<int> &prices) { int n = prices.size(); if(n == 0) return 0; int maxPro = 0; int minBuyPrice = prices[0]; for(int i = 1; i < n; i++){ minBuyPrice = min(minBuyPrice, prices[i]); // 用于记录当前天买进的最小值 minBuyPrice[i+1] = min(minBuyPrice[i], nums[i]) if(maxPro < (prices[i]- minBuyPrice)){ // 全局最大利益是否小于当天的利益 maxPro = prices[i]- minBuyPrice; } } return maxPro; } };
2,不限制买卖次数,收益最大问题
此题是上题的变形,买入卖出的次数没有限制,但是第二次买入必须在第一次卖出的时间节点之后,,此时存在一个局部最优,即 2 4 5 3 6 8,此时8-2一定小于5-2+8-3,,因此就有取数组每次递增的收益即为局部最优,然后所有的局部最优加起来就是全局最优
法一(最简洁,干净,利落):
class Solution { public: int maxProfit(vector<int> &a) { int profit = 0; for (int i = 1; i < a.size(); ++i) { if (a[i] > a[i-1]) { profit += a[i]-a[i-1]; } } return profit; } };
法二:
class Solution { public: int maxProfit(vector<int> &prices) { int n = prices.size(); if(n == 0) return 0; int minBuyPrice = prices[0]; int sumResult = 0; /*for(int i = 0; i < n; i++){ if(i+1 < n && prices[i] >= prices[i+1]){ // 局部最优在于当prices[i] >= prices[i+1] sumResult += prices[i]-minBuyPrice; //既可用prices[i]-minBuyPrices了找到局部最优了--全部加起来就是全局最优解了 minBuyPrice = prices[i+1]; }else{ if(i+1 == n) sumResult += prices[i]-minBuyPrice; } }*/ for(int i = 1; i < n; i++){ if(prices[i] < prices[i-1]){ // 在i处有一个局部最优,所有的局部最优和就是全局最优 sumResult += prices[i-1]- minBuyPrice; minBuyPrice = prices[i]; } } sumResult += prices[n-1]- minBuyPrice; return sumResult; } };3,最多买卖两次股票受益最大问题
此题亦即变形,也就是求交易次数最多为两次。当然有两种方法,第一种暴力,对于每个i,我们求[0,i]与[i,n-1]两次收益,然后求和,遍历i,可以取其中的最大值,需要O(N^2)的时间。第二种方法是动态规划,用两个数组,第一个数组f1[i]用来表示在[0,i]内进行买入卖出的最大收益,用f2[i]表示在[i,n-1]内进行买入卖出的最大收益。然后最大收益即为max(f1[i]+f2[i]),如何求f1[i]和f2[i],,可参考上面的一次买卖股票问题。
class Solution { public: int maxProfit(vector<int> &prices) { int n = prices.size(); if(n <= 1) return 0; vector<int> f1(n); // 表示在[0,i]内进行买入卖出所能获得的最大profit vector<int> f2(n); // 表示在[i, n-1]内进行买入卖出所能获得的最大profit 结果就为max(f1[i]+f2[i]) int minPrice = prices[0]; for(int i = 1; i < n; i++){ minPrice = min(minPrice, prices[i]); f1[i] = max(f1[i-1], prices[i]- minPrice); } int maxPrice = prices[n-1]; for(int i = n-2; i >=0; i--){ // 这里要从后往前遍历 maxPrice = max(maxPrice, prices[i]); f2[i] = max(f2[i+1], maxPrice - prices[i]); } int maxResult = 0; for(int i = 0; i < n; i++) maxResult = max(maxResult, f1[i]+f2[i]); return maxResult; } };4,最多买卖k次股票问题
分析:此题为较难的动态规划问题。首先要第一步就是要转化成动态规划问题, 其次是要写出递归式。我们用local[i][j]表示第i天最多交易j次并且最后一次卖出在第j天所获得的最大利润;global[i][j]用来表示第i天最多交易j次所获得的最大利润。
那么local[i][j]的递归式为:
local[i][j] = max(global[i-1][j-1]+ max(diff, 0), local[i-1][j]+diff).
其中diff = prices[i] - prices[i-1]; global[i-1][j-1]表示第i-1天最多进行j-1次交易获得的最大利润,如果diff大于0,那么那么就加上今天获得利润,diff<0,那么最后一次就可以看做是当天买入并卖出。local[i-1][j]表示第i-1天最多进行j次交易获得的利润,加上diff就等于local[i][j]。 取两者最大值。
global[i][j]的递归式为:
global[i][j] = max(global[i-1][j], local[i][j]);
也就是取第i-1天最多进行j次交易和第i天交易并且最多交易j次且最后一次交易在第i天的利润的最大值。
class Solution { public: int maxProfit(int k, vector<int>& prices) { int n = prices.size(); if(k >= n/2) return maxProfit2(prices); vector<int> lp(k+1); vector<int> gp(k+1); for(int i = 1; i < n; i++){ int diff = prices[i]- prices[i-1]; for(int j = k; j>0; j--){ lp[j] = max(gp[j-1]+max(diff, 0), lp[j]+diff); gp[j] = max(gp[j], lp[j]); } } return gp[k]; } int maxProfit2(vector<int> &prices){ int ans = 0; for(int i = 1; i < prices.size(); i++){ ans += max(prices[i]-prices[i-1], 0); } return ans; } };
最长公共子串问题:
点击打开链接 点击打开链接
动态规划:如果X[i]==Y[j],那么dp[i][j]=dp[i-1][j-1]+1;
如果X[i]!=Y[j],那么dp[i][j]=0;
- 上一篇:没有了
- 下一篇:没有了