-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathm0322.py
133 lines (110 loc) · 3.14 KB
/
m0322.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
"""Coin Change
TAG: dynamic-programming
You are given coins of different denominations and a total amount of money
amount. Write a function to compute the fewest number of coins that you need to
make up that amount. If that amount of money cannot be made up by any
combination of the coins, return -1.
You may assume that you have an infinite number of each kind of coin.
Example 1:
Input: coins = [1,2,5], amount = 11
Output: 3
Explanation: 11 = 5 + 5 + 1
Example 2:
Input: coins = [2], amount = 3
Output: -1
Example 3:
Input: coins = [1], amount = 0
Output: 0
Example 4:
Input: coins = [1], amount = 1
Output: 1
Example 5:
Input: coins = [1], amount = 2
Output: 2
Constraints:
* 1 <= coins.length <= 12
* 1 <= coins[i] <= 231 - 1
* 0 <= amount <= 104
"""
from __future__ import annotations
from dataclasses import dataclass
from enum import Enum, unique
from typing import Sequence, Tuple
@unique
class State(Enum):
DOING = 1
DONE = 2
INVALID = 3
@dataclass(frozen=True)
class FSM:
amount: int
coins: Tuple[int, ...]
actions: Tuple[int, ...]
@property
def state(self) -> State:
if self.amount == 0:
return State.DONE
if self.amount < 0 or not self.coins:
return State.INVALID
return State.DOING
@property
def count(self) -> int:
return len(self.actions)
def explode(self) -> Tuple[FSM, ...]:
assert self.state == State.DOING
seq_actions = [(*self.actions, coin) for coin in self.coins]
seq_amount = [self.amount - coin for coin in self.coins]
return tuple((type(self))(
amount,
self.coins,
actions,
) for actions, amount in zip(seq_actions, seq_amount))
class CoinChange:
@classmethod
def looper(
cls,
inputs: Tuple[FSM, ...],
outputs: Tuple[FSM, ...],
) -> Tuple[FSM, ...]:
if not inputs:
return outputs
fsm = inputs[0]
if fsm.state == State.INVALID:
return cls.looper(inputs[1:], outputs)
if fsm.state == State.DONE:
return cls.looper(inputs[1:], (*outputs, fsm))
return cls.looper((*fsm.explode(), *inputs[1:]), outputs)
def solution(self, coins: Sequence[int], amount: int):
if not amount:
print('trivial, no solution needed')
return 0
fsm = FSM(amount, coins, ())
res = self.looper((fsm, ), ())
if not res:
print('no solution available')
return -1
best = min(res, key=lambda x: x.count)
print(f'the best solution: {best}')
return best.count
if __name__ == '__main__':
ipt_1_1 = [1, 2, 5]
ipt_1_2 = 11
exp_1 = 3
ipt_2_1 = [2]
ipt_2_2 = 3
exp_2 = -1
ipt_3_1 = [1]
ipt_3_2 = 0
exp_3 = 0
ipt_4_1 = [1]
ipt_4_2 = 1
exp_4 = 1
ipt_5_1 = [1]
ipt_5_2 = 2
exp_5 = 2
cc = CoinChange()
assert exp_1 == cc.solution(ipt_1_1, ipt_1_2)
assert exp_2 == cc.solution(ipt_2_1, ipt_2_2)
assert exp_3 == cc.solution(ipt_3_1, ipt_3_2)
assert exp_4 == cc.solution(ipt_4_1, ipt_4_2)
assert exp_5 == cc.solution(ipt_5_1, ipt_5_2)