-
Notifications
You must be signed in to change notification settings - Fork 90
/
Copy Books.py
139 lines (110 loc) · 3.73 KB
/
Copy Books.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
"""
Given an array A of integer with size of n( means n books and number of pages of each book) and k people to copy the
book. You must distribute the continuous id books to one people to copy. (You can give book A[1],A[2] to one people, but
you cannot give book A[1], A[3] to one people, because book A[1] and A[3] is not continuous.) Each person have can copy
one page per minute. Return the number of smallest minutes need to copy all the books.
Example
Given array A = [3,2,4], k = 2.
Return 5 (First person spends 5 minutes to copy book 1 and book 2 and second person spends 4 minutes to copy book 3.)
Challenge
Could you do this in O(n*k) time ?
"""
__author__ = 'Daniel'
class Solution:
def copyBooks(self, pages, k):
"""
DP:
let F[i][j] be the smallest page count with the person[:i] copying the pages[:j]
F[i][j] = min(
max(F[i-1][t], sum(pages[t:j]) \forall t \in [0, j))
)
F[i][j] is monotonous w.r.t. both i and j
left r pointers
move left pointer if the previous people assigned less compared to current person
move r pointer otherwise
Complexity analysis:
for each people,
r forward at most n
l backward at most n
l forward at most 2n
thus, O(n k)
:type pages: List[int]
:type k: int
:rtype: int
"""
n = len(pages)
s = [0 for _ in xrange(n+1)]
for i in xrange(1, n+1):
s[i] = s[i-1] + pages[i-1]
F = [[s[j] for j in xrange(n+1)] for _ in xrange(k+1)] # initialize to upper limit
for i in xrange(2, k+1):
l = 0 # left
r = 1 # right
while r < n+1:
F[i][r] = min(F[i][r],
max(F[i-1][l], s[r]-s[l])
)
if F[i-1][l] < s[r]-s[l] and l < r:
# assign less to current people
l += 1
else:
# assign more to current people
if l > 0: l -= 1
r += 1
return F[-1][-1]
class Solution_TLE:
def copyBooks(self, pages, k):
"""
DP:
let F[i][j] be the smallest page count with the person[:i] copying the pages[:j]
F[i][j] = min(
max(F[i-1][t], sum(pages[t:j]) \forall t \in [0, j))
)
O(n^2 k)
:type pages: List[int]
:type k: int
:rtype: int
"""
n = len(pages)
s = [0 for _ in xrange(n+1)]
for i in xrange(1, n+1):
s[i] = s[i-1] + pages[i-1]
F = [[s[j] for j in xrange(n+1)] for _ in xrange(k+1)] # initialize to upper limit
for i in xrange(2, k+1):
for j in xrange(1, n+1):
F[i][j] = min(
max(F[i-1][t], s[j]-s[t]) for t in xrange(j)
)
return F[-1][-1]
class Solution_search:
def copyBooks(self, pages, k):
"""
:type pages: List[int]
:type k: int
:rtype: int
"""
return self.bisect(pages, k, sum(pages)/k, sum(pages))
def bisect(self, pages, k, lower, upper):
"""
O(n lg sum(p))
"""
while lower < upper:
mid = (lower+upper)/2
if self.valid(pages, k, mid):
upper = mid
else:
lower = mid+1
return lower
def valid(self, pages, k, limit):
cnt = 0
k -= 1
for p in pages:
if p > limit: return False
cnt += p
if cnt > limit:
cnt = p
k -= 1
if k < 0: return False
return True
if __name__ == "__main__":
assert Solution().copyBooks([3, 2], 5) == 3