算法课作业系列7——Best Time to Buy and Sell Stock with Transaction Fee

本文探讨了带有交易费用的最佳买卖时机算法问题,通过两种不同的动态规划方法实现,包括从后往前和从前往后的策略,旨在帮助读者理解如何在给定股价和交易费的情况下最大化利润。

算法课作业系列(7)

Best Time to Buy and Sell Stock with Transaction Fee

题目描述

Your are given an array of integers prices, for which the i-th element is the price of a given stock on day i; and a non-negative integer fee representing a transaction fee.

You may complete as many transactions as you like, but you need to pay the transaction fee for each transaction. You may not buy more than 1 share of a stock at a time (ie. you must sell the stock share before you buy again.)

Return the maximum profit you can make.

Example 1:
Input: prices = [1, 3, 2, 8, 4, 9], fee = 2
Output: 8
Explanation: The maximum profit can be achieved by:
Buying at prices[0] = 1
Selling at prices[3] = 8
Buying at prices[4] = 4
Selling at prices[5] = 9
The total profit is ((8 - 1) - 2) + ((9 - 4) - 2) = 8.

Note:

0 < prices.length <= 50000.
0 < prices[i] < 50000.
0 <= fee < 50000.

思路

其实这道题不算难,主要考的是动态规划的问题,受到背包问题的影响,我自己想到一个算法,时间复杂度为O(n^2),而提交之后给出了超时的判定,在查看讨论区后,自己又根据别人的思想,写好了第二个算法。
值得一提的是,动态规划的算法大多数都比较短小,而思想也是把原问题分解之后求解,和分治类似。在想到动态规划的算法之前可能题目会很复杂,但是一旦找到了状态转移式之后,一切也就迎刃而解了,那么接下来就来看看这两个思路。

  1. 从后往前
    其实这个想法个人认为是很自然而然的,要求最大利益,无非就是当天两种可能,卖或者不卖,先把公式表示出来:
    C(i) = max{p(j) - p(i) - fee + C(j), C(i + 1)} j->(i + 1, n)
    下面来详细说说这个式子,C代表最大利润,p为价格,那么第i天的最大利润就是 第j天卖出加上第j天开始的最大利润i+1天的利润 的最大值。怎么去理解呢?假设我第i天是持有货物的,那么我的利润就是第j天卖出得到的利润,而第j天又可以买入新的货物,因此要加上第j天的最大利润;而如果我第i天手上没有货物,则我第i天的利润也就是第i+1天的利润,具体是第i天买入之后卖出还是第j天什么都不做,就要看这两种结果谁更大就好。我们的前面的值是依赖后面的,因此我们要从最后一天开始往前看,最后一天一定是什么都不做,利润为0,继续往前,只要分解成小问题就可以了,具体的参考上面的公式。
  2. 从前往后
    上面那个是我个人的思考,这里介绍一下另外一个思路,同样的先给出公式
    HOLD(i) = max{HOLD(i - 1), SOLD(i - 1) - p(i)}
    SOLD(i) = max{SOLD(i - 1), HOLD(i - 1) + p(i) - fee}
    上面的HOLD代表第i天持有货物的利润,而SOLD代表第i天没有货物的利润,因此我们可以知道:
    HOLD(i)一种情况下是前一天没有货物,但是当天买了货物;另一种情况是前一天有货物,当天什么都没做,具体看上面的公式
    SOLD(i)一种情况是前一天就没有,当天什么都没做;另一种是前一天有,今天卖出了,可以参考上面公式
    而第一天的话,考虑到没有本金,那么SOLD(0)就应该为0,HOLD(0)就应该为-p(0),这样,我们的算法就设计出来啦!
// Lab 1: CheckingAccount.cpp // Member-function definitions for class CheckingAccount. #include <iostream> using namespace std; ```cpp #include "CheckingAccount.h" // CheckingAccount class definition // constructor initializes balance and transaction fee /* Write the CheckingAccount constructor to call the Account constructor and validate and set the transaction fee value */ CheckingAccount::CheckingAccount(double initialBalance, double fee) : Account(initialBalance) // call base class constructor { if (fee >= 0.0) transactionFee = fee; else { cout << "Error: Transaction fee cannot be negative. Setting fee to 0.0." << endl; transactionFee = 0.0; } } // credit (add) an amount to the account balance and charge fee /* Write the credit member function to call Account's credit function and then charge a fee */ void CheckingAccount::credit(double amount) { Account::credit(amount); // add amount to balance chargeFee(); // charge transaction fee } // debit (subtract) an amount from the account balance and charge fee /* Write the debit member function to call Account's debit function and then charge a fee if it returned true */ bool CheckingAccount::debit(double amount) { bool success = Account::debit(amount); // attempt to withdraw if (success) // if successful { chargeFee(); // charge transaction fee } return success; } // subtract transaction fee /* Write the chargeFee member function to subtract transactionFee from the current balance and display a message */ void CheckingAccount::chargeFee() { Account::debit(transactionFee); // use Account's debit to deduct fee } ``` --- ### **代码概述** 该文件实现了 `CheckingAccount` 类的成员函数,扩展了 `Account` 类的功能,为每次存款或取款操作添加交易手续费机制。 --- ### **代码解析** - 构造函数:使用初始化列表调用基类 `Account` 的构造函数设置余额,并验证手续费是否非负。 - `credit(double amount)`:先调用基类的 `credit` 存入金额,再扣除手续费(通过 `chargeFee`)。 - `debit(double amount)`:尝试取款;仅当成功时才扣除手续费。 - `chargeFee()`:通过调用 `Account::debit(transactionFee)` 扣除手续费,复用已有的安全扣款逻辑。 > ✅ 使用 `Account::debit` 而非直接修改余额,确保不会因手续费导致透支。 --- ### **知识点(列出该代码中遇到的知识点)** 1. **基类构造函数调用(Initialization List)** 派生类通过初始化列表调用基类构造函数,正确初始化继承部分。 2. **函数重写与基类调用(Base Class Function Access)** 使用 `Account::` 显式调用父类方法,实现功能扩展而不重复代码。 3. **条件性费用扣除(Conditional Execution)** 仅在交易成功时收费,提升系统健壮性和用户体验。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值