-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathm0309.py
140 lines (115 loc) · 3.48 KB
/
m0309.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
"""Best Time to Buy and Sell Stock with Cooldown
TAG: dynamic-programming
Say you have an array for which the ith element is the price of a given stock
on day i.
Design an algorithm to find the maximum profit. You may complete as many
transactions as you like (ie, buy one and sell one share of the stock multiple
times) with the following restrictions:
You may not engage in multiple transactions at the same time (ie, you must sell
the stock before you buy again).
After you sell your stock, you cannot buy stock on next day. (ie, cooldown 1
day)
Example:
Input: [1,2,3,0,2]
Output: 3
Explanation: transactions = [buy, sell, cooldown, buy, sell]
"""
from __future__ import annotations
from dataclasses import dataclass
from enum import Enum, unique
from typing import Sequence, Tuple
@unique
class Action(Enum):
IDLE = 0
BUY = 1
SELL = 2
@unique
class Status(Enum):
DOING = 1
DONE = 2
INVALID = 3
@dataclass(frozen=True)
class FSM:
prices: Tuple[int, ...]
stocks: int
profit: int
actions: Tuple[Action, ...]
@property
def status(self) -> Status:
if self.stocks < 0:
return Status.INVALID
if not self.prices:
return Status.DONE
return Status.DOING
@property
def last_action(self) -> Action:
if not self.actions:
return Action.IDLE
return self.actions[-1]
def idle(self) -> FSM:
assert self.status == Status.DOING
return FSM(
self.prices[1:],
self.stocks,
self.profit,
(*self.actions, Action.IDLE),
)
def buy(self) -> FSM:
assert self.status == Status.DOING
price = self.prices[0]
return FSM(
self.prices[1:],
self.stocks + 1,
self.profit - price,
(*self.actions, Action.BUY),
)
def sell(self) -> FSM:
assert self.status == Status.DOING
price = self.prices[0]
return FSM(
self.prices[1:],
self.stocks - 1,
self.profit + price,
(*self.actions, Action.SELL),
)
def explode(self) -> Tuple[FSM, ...]:
assert self.status == Status.DOING
if self.last_action == Action.IDLE and self.stocks >= 1:
return (
self.idle(),
self.buy(),
self.sell(),
)
if self.last_action == Action.IDLE:
return (
self.idle(),
self.buy(),
)
if self.last_action == Action.BUY:
return (
self.idle(),
self.sell(),
)
if self.last_action == Action.SELL:
return (self.idle(), )
raise ValueError('invalid value')
class SellStocks:
@classmethod
def looper(cls, inputs: Sequence[FSM],
outputs: Sequence[FSM]) -> Sequence[FSM]:
if not inputs:
return outputs
fsm = inputs[0]
if fsm.status == Status.INVALID:
return cls.looper(inputs[1:], outputs)
if fsm.status == Status.DONE:
return cls.looper(inputs[1:], (*outputs, fsm))
return cls.looper((*fsm.explode(), *inputs[1:]), outputs)
def solution(self, prices: Sequence[int]) -> int:
fsm = FSM(prices, 0, 0, ())
return max(self.looper((fsm, ), ()), key=lambda x: x.profit)
if __name__ == '__main__':
ipt_1 = [1, 2, 3, 0, 2]
exp_1 = 3
s = SellStocks()
print(s.solution(ipt_1))