股票问题汇总


前言

买卖一次

买卖多次

买卖至多两次

买卖至多k次

含手续费买卖多次

冷冻期买卖多次

/*
	snowy,2022-9-14
	动态规划——股票买卖
	贪心的话是每一步可能有很多选择,但贪心直接只选择可能最优的一种.
	动态规划是列出所有选择后,然后从中选出实际上最优的结果,可将贪心看作动态规划的一种特例

	题目描述:每支股票第i天的交易金额为prices[i],
			 买卖一次,买卖多次,买卖k次,含手续费买卖,有冻结期买卖等
			 多种交易方式,求交易股票的最大利润
*/

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

// 买卖一次 —— 贪心算法,即价格最低时买入,最高时卖出。(常规暴力算法,双重for循环也可)
int maxProfix_one(const vector<int>& Prices) {
	int len = Prices.size();
	int minum = INT_MAX;
	int maxnum = 0;		//最大利润最小值只能为0
	//int profix = 0;

	for (int i = 0; i < len; ++i) {
		minum = min(Prices[i], minum);
		maxnum = max(Prices[i] - minum, maxnum);
	}
	return maxnum;	
}

// 买卖一次 —— 动态规划
int maxProfix_dp_one(const vector<int>& Prices) {
	/*
	动规五部曲
	1. 确定dp数组的下标和含义
	2. 确定递归公式
	3. 初始化dp数组
	4. 确定遍历顺序
	5. 打印dp数组进行验证

	分析:
	后一天获得的利润取决于之间的状态, 
	
	dp数组和递推公式
	每日状态有两种,持有股票和不持有股票利润,则最大利润即是最后一天不持有股票时的最大利润
	dp[i][0] //第i天持有股票所得最多利润, 今天刚买入,-Prices[i], 之前就持有了为dp[][i - 1][0]
	dp[i][0] = max(dp[i - 1][0], - Prices[i])

	dp[i][1] //第i天不持有股票所得最多利润, 今天卖出,为dp[i - 1][0] + Prices[i], 之前就没有,dp[i - 1][1]
	dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + Prices[i])

	初始化:
	dp[0][0] -= -Prices[0];
	dp[0][1] = 0;

	遍历顺序,从前往后遍历

	返回结果:最后一天不持有股票获得利润最大
	*/
	int len = Prices.size();
	vector<vector<int>> dp(len, vector<int>(2, 0));
	dp[0][0] = -Prices[0];
	dp[0][1] = 0;

	for (int i = 1; i < len; ++i) {
		dp[i][0] = max(dp[i - 1][0], -Prices[i]);
		dp[i][1] = max(dp[i - 1][0] + Prices[i], dp[i - 1][1]);
	}
	int ans = dp[len - 1][1];
	return ans;
}

// 买卖多次 —— 贪心算法,只要收集每天的正利润即可
int maxProfix_many(const vector<int>& Prices) {
	int len = Prices.size();
	int profix = 0;		//最大利润最小值只能为0

	for (int i = 0; i < len; ++i) {
		profix = max(Prices[i] -Prices[i - 1], 0);
	}
	return profix;
}

// 买卖多次 —— 动态规划
	/*
	分析:可以买卖多次,当天收益状态取决于前一天的买卖状态,动态规划

	*/
int maxProfix_dp_many(const vector<int>& Prices) {
	/*
	动态规划
	每日持有股票状态:持有和不持有
	持有:dp[i][0], 当天买入dp[i - 1][1] - Prices[i],或者之前就有 dp[i - 1][0]
	dp[i][0] = max(dp[i - 1][1] - Prices[i], dp[i - 1][0])
	不持有:dp[i][1], 当天卖出,dp[i - 1][0] + Prices[i],或者之前就不持有dp[i -1][1]
	dp[i][1] = max(dp[i - 1][0] + Prices[i], dp[i -1][1])

	初始化:
	dp[0][0] -= -Prices[0];
	dp[0][1] = 0;

	遍历顺序,从前往后遍历

	返回结果:最后一天不持有股票的最大值
	*/

	int len = Prices.size();
	vector<vector<int>> dp(len, vector<int>(2, 0));
	dp[0][0] = -Prices[0];
	dp[0][1] = 0;

	for (int i = 1; i < len; ++i) {
		dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - Prices[i]);
		dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + Prices[i]);
	}
	int ans = dp[len - 1][1];
	return ans;
}

// 买卖2次
int maxProfix_dp_2(const vector<int>& Prices) {
	/*
	每天买卖状态:共五种
		0:没有操作,无买卖,dp[i][0]
		dp[i][0] 

		1:第一次持有持有,dp[i][1]
			之前无买卖,然后第一次买入持有:dp[i-1][0] - prices[i],
			延续前一天买入持有状态:dp[i - 1][1]
		dp[i][1] = max(dp[i-1][0] - prices[i], dp[i - 1][1]);

		2:第一次卖出不持有,dp[i][2]
			当天卖出第一次持有买入持有的股票,dp[i][2] = dp[i - 1][1] + prices[i]
			延续前一天第一次卖出不持有的状态, dp[i][2] = dp[i - 1][2]
		dp[i][2] = dp[i][2] = max(dp[i - 1][1] + prices[i], dp[i - 1][2])

		3:第二次买入持有,dp[i][3]
			第一次卖出后买入持有,dp[i - 1][2] - prices[i]
			或者延续之前的第二次买入持有,dp[i - 1][3]
		dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] - prices[i]);

		4:第二次卖出不持有,dp[i][4]
			在第二次买入后卖出,dp[i - 1][3] + prices[i]
			延续之前的第二次卖出不持有,dp[i - 1][4]
		dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + prices[i]);
		
		初始化:
		dp[0][0] = 0;
		dp[0][1] = -prices[0];
		dp[0][2] = 0;// 当利润比0还小时就没有必要卖出
		dp[0][3] = -prices[0];
		dp[0][4] = 0;

		遍历顺序:后一天取决于前一天状态,则从前往后遍历

		返回结果:最后一天第二次不持有的状态,否则没必要第二次持有了
	*/

	if (Prices.size() == 0) return 0;
	vector<vector<int>> dp(Prices.size(), vector<int>(5, 0));
	dp[0][1] = -Prices[0];
	dp[0][3] = -Prices[0];
	for (int i = 1; i < Prices.size(); i++) {
		dp[i][0] = dp[i - 1][0];
		dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] - Prices[i]);
		dp[i][2] = max(dp[i - 1][2], dp[i - 1][1] + Prices[i]);
		dp[i][3] = max(dp[i - 1][3], dp[i - 1][2] - Prices[i]);
		dp[i][4] = max(dp[i - 1][4], dp[i - 1][3] + Prices[i]);
	}
	return dp[Prices.size() - 1][4];
}

// 买卖k次
int maxProfix_dp_k(const vector<int>& Prices, const int& k) {
	/*
		买卖k次 共2*k + 1种状态
		除了0以外,偶数就是卖出不持有,奇数就是买入持有。,每次卖出买入持有状态跟前一次买入卖出持有状态有关
		for (int j = 0; j < 2 * k - 1; j += 2) {
			dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]);
			dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]);
		}

		初始化
		for (int j = 1; j < 2 * k; j += 2) {
			dp[0][j] = -prices[0];
		}
	*/
	if (Prices.size() == 0) return 0;
	vector<vector<int>> dp(Prices.size(), vector<int>(2 * k + 1, 0));
	for (int j = 1; j < 2 * k; j += 2) {
		dp[0][j] = -Prices[0];
	}
	for (int i = 1; i < Prices.size(); i++) {
		for (int j = 0; j < 2 * k - 1; j += 2) {
			dp[i][j + 1] = max(dp[i - 1][j + 1], dp[i - 1][j] - Prices[i]);
			dp[i][j + 2] = max(dp[i - 1][j + 2], dp[i - 1][j + 1] + Prices[i]);
		}
	}
	return dp[Prices.size() - 1][2 * k];
}

//带手续费买卖
int maxProfix_dp_fee(const vector<int>& Prices, const int& fee) {
	/*
	第i天持有股票,dp[i][0], 
		延续之前的股票,dp[i][0] = dp[i - 1][0], 
		之前不持有,今天买入股票, dp[i][0] = dp[i - 1][1] - Prices[i],
	dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - prices[i]);

	第i天不持有股票,dp[i][1],
		延续之前的不持有:dp[i][1] = dp[i - 1][1]
		之前持有,今天卖出 dp[i][1] = dp[i - 1][0] - fee + Prices[i]
	dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + prices[i] - fee);

	返回值有点变化,因为之前都是没手续费,不持有时利益最大,但这里有手续费,不持有可能为负
	*/
	int n = Prices.size();
	vector<vector<int>> dp(n, vector<int>(2, 0));
	dp[0][0] -= Prices[0]; // 持股票
	for (int i = 1; i < n; i++) {
		dp[i][0] = max(dp[i - 1][0], dp[i - 1][1] - Prices[i]);
		dp[i][1] = max(dp[i - 1][1], dp[i - 1][0] + Prices[i] - fee);
	}
	return max(dp[n - 1][0], dp[n - 1][1]);


}


// 冻结期买卖
int maxProfix_dp_stop(const vector<int>& Prices) {
	/* 
	每天的股票持有状态
	0:状态一:买入持有股票状态(今天买入股票,或者是之前就买入了股票然后没有操作):dp[i][0] 
		三种情况:
			没操作,延续昨天的买入持有状态:dp[i][0] = dp[i - 1][0]
			昨天冷冻期,今天买入:dp[i][0] = dp[i - 1][3] - prices[i]
			昨天过了冷冻期,今天买入:dp[i][0] = dp[i - 1][1] - prices[i]
		dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][3], dp[i - 1][1]) - prices[i]);

	卖出不持有股票状态,这里就有两种卖出股票状态
		1:状态二:今天之前就卖出了股票:dp[i][1] 
				昨天卖出不持有,为冷冻期 dp[i - 1][3]
				昨天是延续的之前的不持有状态 :dp[i][1]  = dp[i - 1][1]
			dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]);

		2:状态三:今天卖出了股票:dp[i][2] 
			今天卖出股票,则必须是买入持有状态
			dp[i][2] = dp[i - 1][0] + prices[i];

	3:状态四:今天为冷冻期状态,但冷冻期状态不可持续,只有一天!dp[i][3] 
		昨天卖出:dp[i][3] = dp[i - 1][2];
		dp[i][3] = dp[i - 1][2];

	初始化:
	dp[0][0] = -prices[0];	//买入持有
	dp[0][1]	//保持卖出
	dp[0][2] = 0	//今天卖出
	dp[0][3] = 0	//今天冷冻期,昨天卖出去了
	*/

	int n = Prices.size();
	if (n == 0) return 0;
	vector<vector<int>> dp(n, vector<int>(4, 0));
	dp[0][0] -= Prices[0]; // 持股票
	for (int i = 1; i < n; i++) {
			dp[i][0] = max(dp[i - 1][0], max(dp[i - 1][3], dp[i - 1][1]) - Prices[i]);
			dp[i][1] = max(dp[i - 1][1], dp[i - 1][3]);
			dp[i][2] = dp[i - 1][0] + Prices[i];
			dp[i][3] = dp[i - 1][2];
		}
	return max(dp[n - 1][3], max(dp[n - 1][1], dp[n - 1][2]));
	
}

void test01(const vector<int>& Prices) {
 	cout << "贪心算法" << endl;
	int profix = maxProfix_one(Prices);
	cout << "单次买卖股票最大利润为: " << profix << endl;

	cout << "动态规划" << endl;
	int dp_profix = maxProfix_dp_one(Prices);
	cout << "单次买卖股票最大利润为: " << dp_profix << endl;

	return;
}

void test02(const vector<int>& Prices) {
	//cout << "买卖多次" << endl;
	cout << "动态规划" << endl;
	int dp_profix = maxProfix_dp_many(Prices);
	cout << "多次买卖股票最大利润为: " << dp_profix << endl;

	return;
}

void test03(const vector<int>& Prices) {
	//cout << "买卖多次" << endl;
	cout << "动态规划" << endl;
	int dp_profix = maxProfix_dp_2(Prices);
	cout << "至多2次买卖股票最大利润为: " << dp_profix << endl;

	return;
}

void test04(const vector<int>& Prices, const int& k) {
	//cout << "买卖多次" << endl;
	cout << "动态规划" << endl;
	int dp_profix = maxProfix_dp_k(Prices, k);
	cout << "至多k次买卖股票最大利润为: " << dp_profix << endl;

	return;
}

void test05(const vector<int>& Prices) {
	//cout << "买卖多次" << endl;
	cout << "动态规划" << endl;
	int dp_profix = maxProfix_dp_stop(Prices);
	cout << "冷冻期多次买卖股票最大利润为: " << dp_profix << endl;

	return;
}

void test06(const vector<int>& Prices, const int& k) {
	//cout << "买卖多次" << endl;
	cout << "动态规划" << endl;
	int dp_profix = maxProfix_dp_fee(Prices, k);
	cout << "至多k含手续费多次买卖股票最大利润为: " << dp_profix << endl;

	return;
}


int main() {

	vector<int> Prices = { 1, 3, 2, 8, 4, 9 };
	int k = 2;
	test01(Prices);
	test02(Prices);
	test03(Prices);
	test04(Prices, k);
	test05(Prices);
	int fee = 2;
	test06(Prices, fee);
	system("pause");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时是石头

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值