【Leetcod 动态规划】 子数组最大和一类的问题

53. Maximum Subarray

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

For example, given the array [−2,1,−3,4,−1,2,1,−5,4],
the contiguous subarray [4,−1,2,1] has the largest sum = 6.

该题“连续子数组的最大和”在剑指offer,编程之美上都有,迄今参加的面试中被问过2次,可见其重要性。
思路:
假设把A[i]之前的连续段叫做sum。A[i]是以i结尾的连续子数组的最大和。可以很容易想到:

1. 如果sum>=0,就可以和A[i]拼接在一起构成新的sum'。因为不管A[i]多大,加上一个正数总会更大,这样形成一个新的candidate。
2. 反之,如果sum<0,就没必要和A[I]拼接在一起了。因为不管A[i]多小,加上一个负数总会更小。此时由于题目要求数组连续,所以没法保留原sum,所以只能让sum等于从A[i]开始的新的一段数了,这一段数字形成新的candidate。
3. 将每次得到新的candidate都和全局的result进行比较,那么必然能找到最终答案。
在循环过程中,用result记录历史最大的值。从A[0]到A[n-1]一步一步地进行。
//经典代码Kadane's algorithm
//时间复杂度显然是O(N),因为数组只被扫描了一遍。
bool g_InvalidInput = false;//设置全局变量来区分 1.子数组和的最大值是0 还是 2.无效输入 这两种情况

int maxSubArray(int *data, int n) {
	if(data == NULL || n <= 0) {
		g_InvalidInput = true;
		return 0;
	}
	
    int currSum = 0; //或者初始化为  currSumm = INT_MIN 也OK。
    int result = 0x800000000; //这里一定要赋值result = 0x800000000否则遇到全部为负数的数组将出错。
    for(int i = 0; i < n; ++i){
        if(currSum >= 0)
            currSum += data[i];
		else
            currSum = data[i];
		
        if(currSum > result){
            result = currSum;
        }
    }
    return result;
}


如果这道题目要求返回最大sum子数组的起始下标和结束下标呢?稍微改改就OK。
vector maxSubArray(int *data, int n) {
    int sum = 0; //或者初始化为  summ = INT_MIN 也OK。
    int result = INT_MIN; //这里一定要赋值result=INT_MIN否则遇到全部为负数的数组将出错。
	int start = 0;  
    int end = 0;
	vector res_index;  
    for(int i = 0; i < n; ++i){
        if(sum >= 0)
            sum += data[i];
		else {
			sum = data[i];
			start = i;	
		}
		
        if(sum > result){
            result = sum;
			end = i;
        }
    }
    res_index.push_back(start);  
    res_index.push_back(end);  
    return res_index;  
}


152. Maximum Product Subarray

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.

最大乘积子数组。一次循环其实就可以解决问题,因为数组中出现正数负数,所以我们需要记录到某个位置时的最大值与最小值,因为最小值可能在下一步乘以负数就变成最大值啦。
class Solution {
public:
    int maxProduct(vector& nums) {
        int n = nums.size();
        if(n == 0) return 0;
        
        int currMax, currMin, result;
        currMax = currMin = result = nums[0];
        
        for(int i = 1; i < n; ++i) {
            int tmpMax = currMax * nums[i];
            int tmpMin = currMin * nums[i];
            //如果不用tmpmax,而是写currMax *= nums[i],那么运行完此行,会对下一行结果有影响
            currMax = max(nums[i], max(tmpMax, tmpMin));
            currMin = min(nums[i], min(tmpMax, tmpMin));
            
            result = max(result, currMax);
        }
        return result;
    }
};





198. House Robber

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system connected and it will automatically contact the police if two adjacent houses were broken into on the same night.

Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.

题意翻译:其实就是在所给数组里面找一个子数组,子数组里的每一个元素都不能相邻,然后求其最大和。
递推关系为dp[i] = max(dp[i-2] + num[i], dp[i-1]):如果要偷,上一步必须是不偷;如果不偷,上一次是啥都无所谓。
class Solution {
public:
    int rob(vector& nums) {
        int len = nums.size();
        if(len == 0)
            return 0;
        if(len == 1)
            return nums[0];
        
        vector dp(len, 0);
        dp[0] = nums[0];
        dp[1] = max(nums[0], nums[1]);
        for(int i = 2; i < len; ++i) {
            dp[i] = max(dp[i - 1], dp[i- 2] + nums[i]);
        }
        return dp[len - 1];
    }
};
  因为每一轮迭代只需要触碰到dp的相邻两个选项,所以可以空间压缩为 两个元素 即可。一个为take,一个为 dontTake, 然后记录当前的最大值。
int rob(vector& nums) {
	int take = 0;
	int dontTake = 0;
	int maxProfit = 0; 
	
	for(int i = 0 ; i < nums.size(); ++i){
		take = nonTake + nums[i]; 
		dontTake = maxProfit; 
		maxProfit = max(take, dontTake);
	}
	return maxProfit;
}


213. House Robber II

After robbing those houses on that street, the thief has found himself a new place for his thievery so that he will not get too much attention. This time, all houses at this place are arranged in a circle. That means the first house is the neighbor of the last one. Meanwhile, the security system for these houses remain the same as for those in the previous street.

Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police.

题意:House Robber的题中,首尾也算作相邻。求最大和。
思路:遍历两次:一次不含nums[0],一次不含nums[n-1]。 答案为两者的最大值~
class Solution {
public:
    int rob(vector& nums) {
        int n = nums.size();
        if(n == 0) return 0;
        if(n == 1) return nums[0];
        
        int take = 0, dontTake = 0, profit1 = 0, profit2 = 0;
        for(int i = 0; i < n - 1; ++i) {
            take = dontTake + nums[i];
            dontTake = profit1;
            profit1 = max(take, dontTake);
        }
        take = 0, dontTake = 0;
        for(int i = 1; i < n; ++i) {
            take = dontTake + nums[i];
            dontTake = profit2;
            profit2 = max(take, dontTake);
        }
        return max(profit1, profit2);
    }
};










你可能感兴趣的:(Leetcode,C/C艹,数据结构&算法,【笔面试准备】)