Skip to content

Instantly share code, notes, and snippets.

@py-in-the-sky
Last active March 20, 2018 03:35
Show Gist options
  • Save py-in-the-sky/ab54a2de17b2c5379f6fa1b6946a588d to your computer and use it in GitHub Desktop.
Save py-in-the-sky/ab54a2de17b2c5379f6fa1b6946a588d to your computer and use it in GitHub Desktop.
Solution to Google Code Jam Problem "Lucky Dip"
"""
Lucky Dip: https://codejam.withgoogle.com/codejam/contest/9234486/dashboard#s=p1
Concept Inventory:
* Optimal Play: To play optimally, you choose to redip if your current
dip's value is less than the expected value of redipping. Otherwise,
you stick with your current draw.
* Expected Value of a Dip: The expected value of a dip, given that the player
plays optimally, depends on the probability of drawing a value that is less
than the expected value of redipping. It is the sum of the following two
computations:
* The expected value of redipping times the probability of redipping being
optimal (i.e., of drawing a value less than the expected value of redipping).
I.e., if the expected value of redipping is E and there are L such values,
then: E * L / N.
* The probability of drawing a value greater than or equal to the expected
value of redipping times the expected value of such a draw (i.e., the
probability of redipping being suboptimal times the average of all the
values that are greater than or equal to the expected value of redipping).
I.e., if there are M such values and the sum of all such values is S, then:
S/M * M/N = S/N.
"""
from bisect import bisect_left
### Iteration 3 ###
def solve_even_faster(N, K, V):
"""
Runtime: O(N * logN + K); Extra Space: O(N)
Note that for a given V, expectation_of_next_dip will never
decrease as K increases.
"""
v = sorted(V) # O(N * logN)
n = float(N)
tail_sum = sum(v) # O(N)
expectation_of_next_dip = tail_sum / n
i = 0
for _ in xrange(K): # O(K)
while v[i] < expectation_of_next_dip:
# This loop is O(N) over the life of the algorithm.
tail_sum -= v[i]
i += 1
# x < expectation_of_next_dip for x in v[:i]
# y >= expectation_of_next_dip for y in v[i:]
expectation_of_next_dip = (expectation_of_next_dip * i / n +
tail_sum / n)
return expectation_of_next_dip
### Iteration 2 ###
def solve_fast(N, K, V):
"Runtime: O(logN * (N + K)); Extra Space: O(N)"
v = sorted(V) # O(N * logN)
n = float(N)
v_tail_sums = tail_sums(v) # O(N)
expectation_of_next_dip = v_tail_sums[0] / n
for _ in xrange(K): # O(K * logN)
# Partition values into those less than the expected value of
# redipping and those greater than or equal to it.
# The first i values are less than the expected value of
# redipping; the last (N - i) values are greater than or
# equal to it.
i = bisect_left(v, expectation_of_next_dip) # O(logN)
expectation_of_next_dip = (expectation_of_next_dip * i / n +
v_tail_sums[i] / n)
return expectation_of_next_dip
def tail_sums(arry):
"Runtime: O(N); Extra Space: O(N)"
running_sum = 0
tail_sums_array = [0] * (len(arry) + 1) # O(N)
for i in reversed(xrange(len(arry))): # O(N)
running_sum += arry[i]
tail_sums_array[i] = running_sum
return tail_sums_array
### Iteration 1 ###
def solve_slow(N, K, V):
"Runtime: O(logN * K * N); Extra Space: O(N)"
v = sorted(V) # O(N * logN)
n = float(N)
expectation_of_next_dip = sum(v) / n # O(N)
for _ in xrange(K): # O(K * N * logN)
# Partition values into those less than the expected value of
# redipping and those greater than or equal to it.
# The first i values are less than the expected value of
# redipping; the last (N - i) values are greater than or
# equal to it.
i = bisect_left(v, expectation_of_next_dip) # O(logN)
expectation_of_current_dip = expectation_of_next_dip * i / n
for j in xrange(i, len(v)): # O(N)
expectation_of_current_dip += v[j] / n
expectation_of_next_dip = expectation_of_current_dip
return expectation_of_next_dip
def main():
T = int(raw_input())
for t in xrange(1, T+1):
N, K = map(int, raw_input().split())
V = map(int, raw_input().split())
# print 'Case #{}: {}'.format(t, solve_slow(N, K, V))
# print 'Case #{}: {}'.format(t, solve_fast(N, K, V))
print 'Case #{}: {}'.format(t, solve_even_faster(N, K, V))
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment