From aa71b763accee4e98ede2adaba2df2c1407c8914 Mon Sep 17 00:00:00 2001 From: BrianLusina <12752833+BrianLusina@users.noreply.github.com> Date: Thu, 16 Nov 2023 06:47:23 +0300 Subject: [PATCH 1/2] feat(dp): buy & sell stock --- .../buy_sell_stock}/README.md | 43 ++++++++++++++++ .../buy_sell_stock}/__init__.py | 27 ++++++++++ .../buy_sell_stock}/test_max_profit.py | 0 .../buy_sell_stock}/test_max_profit_three.py | 0 .../buy_sell_stock}/test_max_profit_two.py | 0 .../test_max_profit_with_fee.py | 51 +++++++++++++++++++ 6 files changed, 121 insertions(+) rename {puzzles/arrays/max_profit => algorithms/dynamic_programming/buy_sell_stock}/README.md (73%) rename {puzzles/arrays/max_profit => algorithms/dynamic_programming/buy_sell_stock}/__init__.py (69%) rename {puzzles/arrays/max_profit => algorithms/dynamic_programming/buy_sell_stock}/test_max_profit.py (100%) rename {puzzles/arrays/max_profit => algorithms/dynamic_programming/buy_sell_stock}/test_max_profit_three.py (100%) rename {puzzles/arrays/max_profit => algorithms/dynamic_programming/buy_sell_stock}/test_max_profit_two.py (100%) create mode 100644 algorithms/dynamic_programming/buy_sell_stock/test_max_profit_with_fee.py diff --git a/puzzles/arrays/max_profit/README.md b/algorithms/dynamic_programming/buy_sell_stock/README.md similarity index 73% rename from puzzles/arrays/max_profit/README.md rename to algorithms/dynamic_programming/buy_sell_stock/README.md index a63f3b61..1eea3443 100644 --- a/puzzles/arrays/max_profit/README.md +++ b/algorithms/dynamic_programming/buy_sell_stock/README.md @@ -85,3 +85,46 @@ Input: prices = [7,6,4,3,1] Output: 0 Explanation: In this case, no transaction is done, i.e. max profit = 0. ``` + +--- + +# Best Time To Buy and Sell Stock With Transaction Fee + +You are given an array prices where prices[i] is the price of a given stock on the ith day, and an integer fee +representing a transaction fee. + +Find the maximum profit you can achieve. You may complete as many transactions as you like, but you need to pay the +transaction fee for each transaction. + +Note: + +You may not engage in multiple transactions simultaneously (i.e., you must sell the stock before you buy again). +The transaction fee is only charged once for each stock purchase and sale. + +Example 1: + +```text +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. +``` + +Example 2: + +```text +Input: prices = [1,3,7,5,10,3], fee = 3 +Output: 6 +``` + +# Related topics + +- Arrays +- Dynamic Programming +- Greedy + + diff --git a/puzzles/arrays/max_profit/__init__.py b/algorithms/dynamic_programming/buy_sell_stock/__init__.py similarity index 69% rename from puzzles/arrays/max_profit/__init__.py rename to algorithms/dynamic_programming/buy_sell_stock/__init__.py index a9a244db..4940803e 100644 --- a/puzzles/arrays/max_profit/__init__.py +++ b/algorithms/dynamic_programming/buy_sell_stock/__init__.py @@ -1,3 +1,4 @@ +import math from typing import List @@ -93,3 +94,29 @@ def max_profit_3(prices: List[int]) -> int: profit = max(profit, forward_profit[day] + backward_profit[day]) return profit + + +def max_profit_with_fee(prices: List[int], fee: int) -> int: + # initially, there is no cash + initial_cash = -math.inf + # initial profit is 0 + current_profit = 0 + + for price in prices: + # Maximum cash in hand with shares + # Either + # 1. withold prev share in which case your cash in hand will not change, + # 2. or assume there was no currently bought share but you want to buy it today - + # In this case: your current cash in hand with shares will be your previous cash + # in hand without shares minus buying price of the share today. + initial_cash = max(initial_cash, current_profit - price) + + # Maximum cash in hand without shares + # Either + # 1. withold money without shares in which case your cash in hand will not change, + # 2. or assume you previously bought the share and you are going to sell that today - + # In this case : your current cash in hand without shares will be your previous cash + # in hand with shares plus the current selling price minus transaction fee + current_profit = max(current_profit, initial_cash + price - fee) + + return current_profit diff --git a/puzzles/arrays/max_profit/test_max_profit.py b/algorithms/dynamic_programming/buy_sell_stock/test_max_profit.py similarity index 100% rename from puzzles/arrays/max_profit/test_max_profit.py rename to algorithms/dynamic_programming/buy_sell_stock/test_max_profit.py diff --git a/puzzles/arrays/max_profit/test_max_profit_three.py b/algorithms/dynamic_programming/buy_sell_stock/test_max_profit_three.py similarity index 100% rename from puzzles/arrays/max_profit/test_max_profit_three.py rename to algorithms/dynamic_programming/buy_sell_stock/test_max_profit_three.py diff --git a/puzzles/arrays/max_profit/test_max_profit_two.py b/algorithms/dynamic_programming/buy_sell_stock/test_max_profit_two.py similarity index 100% rename from puzzles/arrays/max_profit/test_max_profit_two.py rename to algorithms/dynamic_programming/buy_sell_stock/test_max_profit_two.py diff --git a/algorithms/dynamic_programming/buy_sell_stock/test_max_profit_with_fee.py b/algorithms/dynamic_programming/buy_sell_stock/test_max_profit_with_fee.py new file mode 100644 index 00000000..c85119db --- /dev/null +++ b/algorithms/dynamic_programming/buy_sell_stock/test_max_profit_with_fee.py @@ -0,0 +1,51 @@ +import unittest + +from . import max_profit_with_fee + + +class MaxProfitWithFeeTestCases(unittest.TestCase): + def test_empty_array(self): + nums = [] + expected = 0 + actual = max_profit_with_fee(nums, 4) + self.assertEqual(expected, actual) + + def test_prices_are_same(self): + nums = [1, 1, 1, 1] + expected = 0 + actual = max_profit_with_fee(nums, 4) + self.assertEqual(expected, actual) + + def test_single_element_array(self): + nums = [1] + expected = 0 + actual = max_profit_with_fee(nums, 1) + self.assertEqual(expected, actual) + + def test_4(self): + """should return 8 for prices=1,3,2,8,4,9 and fee of 2""" + nums = [1, 3, 2, 8, 4, 9] + fee = 2 + expected = 8 + actual = max_profit_with_fee(nums, fee) + self.assertEqual(expected, actual) + + def test_5(self): + """should return 6 for prices=1,3,7,5,10,3 and fee of 3""" + nums = [1, 3, 7, 5, 10, 3] + fee = 3 + expected = 6 + actual = max_profit_with_fee(nums, fee) + self.assertEqual(expected, actual) + + def test_6(self): + """should return 13 for prices=[1,4,6,2,8,3,10,14] and fee of 3""" + nums = [1, 4, 6, 2, 8, 3, 10, 14] + fee = 3 + expected = 13 + actual = max_profit_with_fee(nums, fee) + self.assertEqual(expected, actual) + + +if __name__ == "__main__": + unittest.main() From ebf7fb3ed729f2e0ba2d41f16333f2ca916c7900 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Thu, 16 Nov 2023 03:47:48 +0000 Subject: [PATCH 2/2] updating DIRECTORY.md --- DIRECTORY.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/DIRECTORY.md b/DIRECTORY.md index f783f918..d46b03e7 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -29,6 +29,11 @@ * Dollar Bills * [Make Change](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dollar_bills/make_change.py) * Dynamic Programming + * Buy Sell Stock + * [Test Max Profit](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dynamic_programming/buy_sell_stock/test_max_profit.py) + * [Test Max Profit Three](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dynamic_programming/buy_sell_stock/test_max_profit_three.py) + * [Test Max Profit Two](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dynamic_programming/buy_sell_stock/test_max_profit_two.py) + * [Test Max Profit With Fee](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dynamic_programming/buy_sell_stock/test_max_profit_with_fee.py) * Domino Tromino Tiling * [Test Domino Tromino Tiling](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/dynamic_programming/domino_tromino_tiling/test_domino_tromino_tiling.py) * Duffle Bug Value @@ -335,10 +340,6 @@ * [Test Max Consecutive Ones](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/max_consecutive_ones/test_max_consecutive_ones.py) * Max Number Of Ksum Pairs * [Test Max Number Of Ksum Pairs](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/max_number_of_ksum_pairs/test_max_number_of_ksum_pairs.py) - * Max Profit - * [Test Max Profit](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/max_profit/test_max_profit.py) - * [Test Max Profit Three](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/max_profit/test_max_profit_three.py) - * [Test Max Profit Two](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/max_profit/test_max_profit_two.py) * Maximum Average Subarray * [Test Max Average Subarray](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/maximum_average_subarray/test_max_average_subarray.py) * Maxlen Contiguous Binary Subarray