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;
}
}
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)