换个体位 豁然开朗 --- Jump Game &&Best Time to Buy and Sell Stock VI

1、Jump Game (Leetcode 55)

题目大意 

      给定一个数组a[], a[i]表示 从当前点能向前跳的最大距离,问是否能跳到数组a的最后一个位置 。

解题思路

      很容易想到穷举,依次判断第 i 个点前的全部点能否到达i点,若存在任意一个可达即表明此点可达,由此得到O(n*n)的算法如下

public class Solution {
	int[] step;
    boolean ans;
    public boolean canJump(int[] nums) {
    	int n = nums.length;
        int index = 0;
        step = new int[n];
        ans = false;
        if (n == 1) return true;
        if (nums[0] == 0) return false;
        step[0] = 1;
        for (int i = 0; i < n ; i ++){
           for (int j = 0 ; j < i; j ++){
             if (step[j] == 1 && i-j <= nums[j]){
        	step[i] = 1;
        	break;
             }	
           }
        }
    	if (step[n-1] == 1) ans = true;
    	return ans;
    }
}
最近做的leetcode大多乏善可陈,不过这里有个地方值得一提,上述遍历会TLE,因为如果第二层循环顺序遍历则要求步数a[i]的数值较大,存在不必要的开销,因此简单的换个体位,将第二层循环反向遍历即可AC,题目不难,但告诉我们前面不通就试试后面通不通(捡肥皂?),逆向思维往往能有意外之喜。

由此引出第二题

2、Best Time to Buy and Sell Stock VI (Leetcode 188)

题目大意

     给定数组 a和整数 k,a[i]表示第i天的股票价格,k表示最多买卖k次,求最大的利润。

解题思路

     当前状态仅由前面的状态决定,符合DP的无后效性,因此考虑 DP的方法。首先想到数组profits[i][j]表示第i天交易至少j次的最大利润,则对于当前第i天的价格,存在卖或者不卖两种状态,由此得到转移方程 

                                                              profits[i][j]  =  max(profits[i][j], max(profits[m][j], progits[m][j-1]+prices[i]-prices[m]))   (m=1...i)

显然这种方法非常容易想到,但是代码需要三层循环

代码

public class Solution {
    public int maxProfit(int k, int[] prices) {
    	int n = prices.length;
        int[][] profits = new int[n][k+1];
        int ans = 0;
        for (int i = 0; i < n; i ++){
        	for (int m = 0; m < i; m ++){//(int)Math.min(i, k)
	        	for (int j = 1; j <= k; j ++){
	        		//System.out.println(profits[m][j]+prices[i]-prices[m]);
	        		profits[i][j] =  Math.max(profits[i][j],Math.max(profits[m][j], profits[m][j-1]+prices[i]-prices[m]));
	        		ans = Math.max(ans, profits[i][j]);
	        		//System.out.println(prices[i]+"  "+prices[m]);
	        	}
        	}
        }
        /*for (int i = 0; i < n; i ++){
        	
	        	for (int j = 0; j <= k; j ++){
	        		System.out.print(profits[i][j]+"  ");
	        	}
	        	System.out.println("");
	        }*/
        return ans;
    }
}

原本以为应该是TLE,结果却是MLE,一般二维数组建议将大维度放在二维,调换二维数组位置后再次提交,这次如愿以偿地获得了TLE,一看结果已经过了209组数据,并且那组数据的k是远大于总天数n的,既然这样那最多也就交易n/2次,因此对代码进行了简单的优化,如果k比n/2大,则一但存在盈利就买卖(贪心算法),显然这样最终能够获得最大利润,即

 

if (k >= n/2){
    		for (int i = 1; i < n; i ++){
    			ans += Math.max(prices[i]-prices[i-1], 0);
    		}
    		return ans;
    	}

事实证明Leetcode Hard级别的题目不是那么容易水过的 ,OJ判定TLE, WTF!一看数据被卡在最后一组(第211组),此组数据中k小于n,刚好跳过这个优化,多么蛋疼的数据。草泥马狂奔过后还得解决问题,显然要减少循环次数,但是按第i天依次向后需要遍历买卖的位置,只刚正面估计不行,得换个体位,从k入手,在至少k次这个条件上写文章,即 考虑至少进行1次、2次、3次......k次的最大利润 ,由此得到代码如下

public class Solution {
    public int maxProfit(int k, int[] prices) {
    	int n = prices.length;
    	if (n == 0)  return 0;
    	int ans = 0;
    	if (k >= n/2){
    		for (int i = 1; i < n; i ++){
    			ans += Math.max(prices[i]-prices[i-1], 0);
    		}
    		return ans;
    	}
        int[][] profits = new int[k+1][n];
        
        
        
        	//for (int m = 0; m < i; m ++){//(int)Math.min(i, k)
        	
    	for (int j = 1; j <= k; j ++){
    		int curProfit = -1*prices[0];
    		for (int i = 1; i < n; i ++){
    			
        		//System.out.println(profits[m][j]+prices[i]-prices[m]);
        		
        		profits[j][i] =  Math.max(profits[j][i-1],prices[i]+curProfit);
        		curProfit = Math.max(curProfit,profits[j-1][i-1]-prices[i]);
        		//ans = Math.max(ans, profits[j][i]);
        		//System.out.println(prices[i]+"  "+prices[m]);
    		}
        	//}6 2 3 2 6 5 0 3
        }
    	for (int j = 0; j <= k; j ++){
    		ans = Math.max(ans, profits[j][n-1]);
    	}
        /*for (int i = 0; i < n; i ++){
        	
	        	for (int j = 0; j <= k; j ++){
	        		System.out.print(profits[i][j]+"  ");
	        	}
	        	System.out.println("");
	        }*/
        return ans;
    }
}

总结

正面刚不动时,题问考思度角个换,时刻想着变换新姿势、新体位,思维才能更加开(wei)(yin)阔(suo)(dang)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值