-
Notifications
You must be signed in to change notification settings - Fork 90
/
Coins in a Line III.py
141 lines (109 loc) · 4.38 KB
/
Coins in a Line III.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
141
"""
There are n coins in a line. Two players take turns to take a coin from one of the ends of the line until there are no
more coins left. The player with the larger amount of money wins.
Could you please decide the first player will win or lose?
Have you met this question in a real interview? Yes
Example
Given array A = [3,2,2], return true.
Given array A = [1,2,4], return true.
Given array A = [1,20,4], return false.
Challenge
Follow Up Question:
If n is even. Is there any hacky algorithm that can decide whether first player will win or lose in O(1) memory and O(n)
time?
"""
__author__ = 'Daniel'
class Solution:
def firstWillWin_MLE(self, values):
"""
DP with Game theory
Let F_{i, j}^{p} represents the max value he can get in sub-array A[i..j] for person p.
DP formula:
F_{i, j}^{0} = max(A_i + sum - F_{i+1, j}^{1},
A_j + sum - F_{i, j-1}^{1}
)
Sometimes assuming the opponent will carry out the best strategy eliminate stochastic process
Memory Limit Exceeded
:param values: a list of integers
:return: a boolean which equals to True if the first player will win
"""
n = len(values)
if n == 1:
return True
F = [[[0 for _ in xrange(n)] for _ in xrange(n)] for _ in xrange(2)]
s = [0 for _ in xrange(n+1)]
for i in xrange(1, n+1):
s[i] = s[i-1]+values[i-1]
for i in xrange(n):
for p in xrange(2):
F[p][i][i] = values[i]
for i in xrange(n-2, -1, -1):
for j in xrange(i+1, n):
for p in xrange(2):
F[p][i][j] = max(
values[i]+s[j+1]-s[i+1]-F[1^p][i+1][j],
values[j]+s[j]-s[i]-F[1^p][i][j-1]
)
return F[0][0][n-1]>min(F[1][0][n-2], F[1][1][n-1])
def firstWillWinNormalCase(self, values):
"""
optimize data structure
General solution to this question, but it can be optimized further by using tricks when the number of coins is
even number.
Time Limit Exceeded
:param values: a list of integers
:return: a boolean which equals to True if the first player will win
"""
n = len(values)
if n == 1:
return True
SZ = 4
F = [[[0 for _ in xrange(SZ)] for _ in xrange(SZ)] for _ in xrange(2)]
s = [0 for _ in xrange(n+1)]
for i in xrange(1, n+1):
s[i] = s[i-1]+values[i-1]
for i in xrange(n-2, -1, -1):
for j in xrange(i+1, n):
for p in xrange(2):
if j == i+1:
a = values[i+1]
b = values[i]
else:
a = F[1^p][(i+1)%SZ][j%SZ]
b = F[1^p][i%SZ][(j-1)%SZ]
F[p][i%SZ][j%SZ] = max(
values[i]+s[j+1]-s[i+1]-a,
values[j]+s[j]-s[i]-b
)
return F[0][0][(n-1)%SZ] > min(F[1][0][(n-2)%SZ], F[1][1][(n-1)%SZ])
def firstWillWin(self, values):
"""
:param values: a list of integers
:return: a boolean which equals to True if the first player will win
"""
n = len(values)
if n%2 == 0 and self.firstWillWinEven(values):
return True
return self.firstWillWinNormalCase(values)
def firstWillWinEven(self, values):
"""
odd_s: sum of values at odd position
even_s: sum of values at even position
if odd_s == even_s, the first mover cannot win if the other player mimics the first player
if odd_s > even_s, the first mover chooses the odd position values, and FORCE the other player choose the even
position values. The strategy and outcome are similar when even_s > odd_s.
:param values:
:return:
"""
odd_s = 0
even_s = 0
for i in xrange(len(values)):
if i%2 == 0:
even_s += values[i]
else:
odd_s += values[i]
return odd_s != even_s
if __name__ == "__main__":
assert Solution().firstWillWin([3, 2, 2]) is True
assert Solution().firstWillWin([1, 20, 4]) is False
assert Solution().firstWillWin([1, 2, 3, 4, 5, 6, 7, 8, 13, 11, 10, 9]) is True