leetcode(121)(122)(123)(188) Best Time to Buy and Sell Stock I/II/III/IV JAVA代码

原题链接:

https://leetcode.com/problems/best-time-to-buy-and-sell-stock

https://leetcode.com/problems/best-time-to-buy-and-sell-stock-II

https://leetcode.com/problems/best-time-to-buy-and-sell-stock-III

https://leetcode.com/problems/best-time-to-buy-and-sell-stock-IV

第一题

这道题比较简单,只允许交易一次,得到最大收益。只要找到两天价格差最大即可

按照此思路,代码如下:

package leetcode;

public class BestTimetoBuyAndSellStock {

	// https://leetcode.com/problems/best-time-to-buy-and-sell-stock/
	
	public static int maxProfit(int[] prices) {
		int len = prices.length;
		if (prices == null || len <= 1) {
			return 0;
		}
		int minPrice = prices[0]; // 最小价格初始化
		int diff = prices[1] - prices[0];// 价格差
		for (int i = 2; i < len; i++) {
			minPrice = Math.min(prices[i-1], minPrice);
			if(diff<prices[i]-minPrice){
				diff=prices[i]-minPrice;
			}
		}
		if(diff<0){
			return 0;
		}
		return diff;
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] prices = { 1, 3, 5, 6, 1, 2, 9 };
		System.out.println(maxProfit(prices) + "");
	}

}


上面先找到最小价格,然后用最小价格与当前搜索到的位置进行减法操作,最后得到最大的价格差。

还有一种解法,维护两个变量,一个代表当前最大收益local,一个代表全局最大收益global,local是上一次当前收益加上后面两天收益的价差得到,全局收益是指全局收益与此次当前收益取最大值。这样,local的值实质上是此时卖出价格与第一次买入时的价差,global可以记录价差的最大值。大家可以运行代码单步调试来理解这个方法,代码如下:

package leetcode;

public class BestTimetoBuyAndSellStock {

	// https://leetcode.com/problems/best-time-to-buy-and-sell-stock/
	public static int maxProfit(int[] prices) {
		if (prices == null || prices.length <= 1) {
			return 0;
		}
		int local = 0;
		int global = 0;
		for (int i = 0; i < prices.length - 1; i++) {
			local = Math.max(local + prices[i + 1] - prices[i], 0);// 当前最大收益
			global = Math.max(local, global);// 全局最大收益
		}
		return global;
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] prices = { 1, 3, 5, 6, 1, 2, 9 };
		System.out.println(maxProfit(prices) + "");
	}

}


第二题:

这里同第一次差别为可以购买无数次,这道题可以这样想,根据第一题第二种解法,可以知道当前最大收益本地值,当时只允许买卖一次,所以用一个global来记录最大收益。现在可以允许买卖无数次,所以不需要global去记录最大收益,只要收益为正的时候,就可以进行买入卖出。,所以只要前后两次价差为正即可加到收益中去。代码如下:

package leetcode;

import java.lang.reflect.Array;

public class BestTimeToBuyAndSellStockII {

	// https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/
	public static int maxProfit(int[] prices) {
		if (prices == null || prices.length == 0) {
			return 0;
		}
		int len = prices.length;
		int maxProfit = 0;
		for (int i = 1; i < len; i++) {
			int tempProfit = prices[i] - prices[i - 1];
			if (tempProfit > 0) {
				maxProfit += tempProfit;
			}
		}
		return maxProfit;
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] prices = { 1 };
		System.out.println(maxProfit(prices) + "");
	}

}


第三题:

可以买入两次。这时可以用动态规划的算法来做了,但是我到第四题的时候可以买入k次条件时再用动态规划,这里用另外一种方法。

这里我们创建两个数组ArrayA与ArrayB,一个记录从前向后遍历得到的最大价差,一个记录从后向前遍历得到的最大价差。最后两个相加后得到的最大值即为最大收益。代码如下:

package leetcode;

public class BestTimetoBuyAndSellStockIII {

	// https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/
	public static int maxProfit(int[] prices) {
		if (prices == null || prices.length == 0) {
			return 0;
		}
		int len = prices.length;
		int sum = 0;
		int min = prices[0];
		int[] ArrayA = new int[len];
		for (int i = 1; i < prices.length; i++) {
			ArrayA[i] = prices[i] - min;
			ArrayA[i] = ArrayA[i] > ArrayA[i - 1] ? ArrayA[i] : ArrayA[i - 1];
			if (prices[i] < min) {
				min = prices[i];
			}
		}
		int max = prices[prices.length - 1];
		int[] ArrayB = new int[len];
		for (int i = len - 1; i > 0; i--) {
			ArrayB[i] = max - prices[i];
			ArrayB[i] = ArrayB[i] > ArrayB[i - 1] ? ArrayB[i] : ArrayB[i - 1];
			if (prices[i] > max) {
				max = prices[i];
			}
		}
		for (int i = 0; i < len; i++) {
			sum = Math.max(ArrayA[i] + ArrayB[i], sum);
		}
		return sum;
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] prices = { 1, 3, 5, 6, 1, 2, 9 };
		System.out.println(maxProfit(prices) + "");
	}

}
这个思路很容易理解,前后两次价差相加。


第四题:

买入k次,这次就只能用动态规划的思想了。之前不了解动态规划的同学,可以先看背包问题九讲!!!,讲的很好,之后我也回把背包问题的代码贴出来。

背包问题九讲链接地址:http://love-oriented.com/pack/(必看)

首先先按照第二题思路,求得最大收益值与最大交易次数(无限次条件)。如果交易次数k大于最大交易次数,则最终结果就是最大收益。

如果k小于最大交易次数,则使用动态规划的方法求解收益最大值。

第四题代码:

package leetcode;

public class BestTimetoBuyAndSellStockIV {

	// https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iv/
	public static int maxProwt(int k, int[] prices) {

		int days = prices.length;
		int tradeCount = 0;// 交易次数
		int profitCount = 0;// 收益
		int rangeProfitCount = 0;
		for (int i = 1; i < days; i++) {
			if (prices[i - 1] < prices[i]) {
				// 两天之间收益为正值
				rangeProfitCount += prices[i] - prices[i - 1];
				if (i == days - 1) {// 最后一天
					profitCount += rangeProfitCount;
					tradeCount += 1; // 交易次数加一
				}
			} else if (rangeProfitCount > 0) {
				profitCount += rangeProfitCount; // 收益加上
				tradeCount += 1; // 交易次数加一
				rangeProfitCount = 0;
			}
		}
		if (k >= tradeCount) {// 如果k交易次数大于等译交易总次数,直接返回最大收益
			return profitCount;
		}
		int[][] global = new int[k + 1][days];
		int[][] local = new int[k + 1][days];
		// 动态规划
		for (int i = 1; i <= k; i++) {
			for (int j = 1; j < days; j++) {
				int diff = prices[j] - prices[j - 1];
				local[i][j] = Math.max(global[i - 1][j - 1], local[i][j - 1] + diff);
				global[i][j] = Math.max(global[i][j - 1], local[i][j]);
			}
		}
		return global[global.length - 1][global[0].length - 1];
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] prices = { 1, 3, 5, 6, 1, 2, 9 };
		System.out.println(maxProwt(3, prices) + "");
	}

}

附:背包问题代码(包含01背包,完全背包,多重背包求解方法)

package pack;

import java.util.ArrayList;
import java.util.List;

import org.omg.CORBA.SystemException;

public class Pack {

	private static int N = 4; // 物品个数
	private static int V = 10; // 背包最大容量
	private int[] f;// 前i件物品放入容量为j的背包得到的最大价值f[j]
	private static int G=2;//分组背包问题组数
	// 状态转移方程f[v]=max{f[v],f[v-cost]+weight};

	public Pack() {
		super();
		resetF();
	}

	private void resetF() {
		f = new int[V + 1];
		for (int i = 0; i < f.length; i++) {
			f[i] = 0;
		}
	}

	/**
	 * 01背包
	 * 
	 * @param cost
	 *            消耗容量
	 * @param weight
	 *            体积
	 * @return
	 */
	private void ZeroOnePack(int cost, int weight) {
		for (int v = V; v >= cost; v--) {
			f[v] = Math.max(f[v], f[v - cost] + weight);
		}
	}

	/**
	 * 完全背包
	 * 
	 * @param cost
	 *            消耗容量
	 * @param weight
	 *            体积
	 * @return
	 */
	private void CompletePack(int cost, int weight) {
		for (int v = cost; v <= V; v++) {
			f[v] = Math.max(f[v], f[v - cost] + weight);
		}
	}

	/**
	 * 多重背包
	 * 
	 * @param cost
	 * @param weight
	 * @param amount
	 */
	private void MultiplePack(int cost, int weight, int amount) {
		if (cost * amount >= V) {
			CompletePack(cost, weight);
			return;
		}
		int k = 1;
		while (k < amount) {
			ZeroOnePack(k * cost, k * weight);
			amount = amount - k;
			k = k * 2;
		}
		ZeroOnePack(amount * cost, amount * weight);
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		int[] cost = { 6, 3, 4, 2 };
		int[] weight = { 30, 14, 16, 9 };
		int[] amount = { 0, 1, 2, 3 };
		Pack zo = new Pack();
		/************01背包************/
		zo.resetF();
		for (int i = 0; i < N; i++) {
			zo.ZeroOnePack(cost[i], weight[i]);
		}
		System.out.println(zo.f[V] + "");
		/************完全背包 ************/
		zo.resetF();
		for (int i = 0; i < N; i++) {
			zo.CompletePack(cost[i], weight[i]);
		}
		System.out.println(zo.f[V] + "");
		/************多重背包************/
		zo.resetF();
		for (int i = 0; i < N; i++) {
			zo.MultiplePack(cost[i], weight[i], amount[i]);
		}
		System.out.println(zo.f[V] + "");
	}

}

同时还有一篇讲动态规划的文章推荐给大家: http://www.cnblogs.com/sdjl/articles/1274312.html

同时附录出文章中的C++代码:

#include<iostream>
#include<fstream>
using namespace std;

//http://www.cnblogs.com/sdjl/articles/1274312.html
const int max_n=100;//程序支持的最多金矿数
const int max_people=10000;//程序支持的最多人数

int n;//金矿数
int peopleTotal;//可以用于挖金子的人数
int peopleNeed[max_n];//每座金矿需要的人数
int gold[max_n];//每座金矿能够挖出来的金子数
int maxGold[max_people][max_n];//maxGold[i][j]保存了i个人挖前j个金矿能够得到的最大金子数,等于-1表示未知

//初始化数据
void init(){
    ifstream inputFile("beibao.in");
    inputFile>>peopleTotal>>n;
    for(int i=0;i<n;i++){
        inputFile>>peopleNeed[i]>>gold[i];
    }
    inputFile.close();

    for(int i=0;i<=peopleTotal;i++){
        for(int j=0;j<n;j++){
            maxGold[i][j]=-1;//等于-1时表示未知
        }
    }
}

//获得在仅有people个人和前mineNum个金矿时能够得到的最大金子数
int GetMaxGold(int people,int mineNum){
    int retMaxGold; //返回的最大金子数
    if(maxGold[people][mineNum]!=-1){//如果曾经这个问题已经计算过
        retMaxGold=maxGold[people][mineNum];//获得保存起来的值
    }
    else if(mineNum==0){//如果仅有一个金矿
        if(people>peopleNeed[mineNum]){
            retMaxGold=gold[mineNum];
        }else{//否则一个矿也不能开采
            retMaxGold=0;
        }
    }
    else if(people>=peopleNeed[mineNum]){//人数够开采此矿
        retMaxGold=max(GetMaxGold(people-peopleNeed[mineNum],mineNum-1)+gold[mineNum],GetMaxGold(people,mineNum-1));//两种情况,要么开采此矿剩下的人采其它矿;要么不开采此矿,所有人开采下一个矿.两者取最大值
    }
    else{//剩下的人无法开采此矿,直接开采下一个矿
        retMaxGold=GetMaxGold(people,mineNum-1);
    }

    maxGold[people][mineNum]=retMaxGold;//记录下当前人数能采mineNum号矿的最大值
    return retMaxGold;
}

int main(){
    init();
    cout<<GetMaxGold(peopleTotal,n-1);
    //return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值