LuckyDip – Google Kick Start Round A, 2018

For Small Dataset

The following should work fine for small data set, even if without the dynamic programming.

import statistics

def mean(numbers):
    if len(numbers) == 0:
        return 0
    return statistics.mean(numbers)

def dip(numbers, time, d): 
    if time == 0:
        d[0] = mean(numbers)
        return d[0]
    
    exp_val = d[time-1] if (time - 1) in d else dip(numbers, time - 1, d)
    larger = [i for i in numbers if i > exp_val]
    p = len(larger) / len(numbers)
    return mean(larger) * p +  exp_val * (1 - p)


N = int(input())
for n in range(N):
    [count, time] = [int(i) for i in input().split()]
    numbers = [int(i) for i in input().split()]
    d = dict()
    res = dip(numbers, time, d)
    print("Case #{}: {:.6f}".format(n + 1, res))

For Large Dataset

The most consuming part in the previous one is to find out the “larger” numbers, we find them all in the small dataset solution. But when the dataset grows larger, that does not work. To make that easier, we can sort the data once, and binary sort afterwards. So that the computation can be reduced to O(Nlg(N) + N + KlgN) = O((N + K)lgN).

Failed Attempt

What I got was always an RE error, maybe I should try later.

import statistics
from math import floor

def mean(numbers):
    if len(numbers) == 0:
        return 0
    return statistics.mean(numbers)

def larger_index_utility(numbers, number, front, end):
    if front >= end:
        return front

    mid = floor((front + end) / 2)

    if number < numbers[mid]:
        return larger_index_utility(numbers, number, front, mid - 1)
    if number == numbers[mid]:
        return mid
    return larger_index_utility(numbers, number, mid + 1, end)

def larger_index(numbers, number):
    index = larger_index_utility(numbers, number, 0, len(numbers) - 1)
    if numbers[index] >= number:
        return index
    return index + 1


def dip(numbers, time, d, suffix): 
    if time == 0:
        d[0] = mean(numbers)
        return d[0]
    
    if (time - 1) in d:
        exp_val = d[time - 1]
    else:
        exp_val =  dip(numbers, time - 1, d, suffix)
        d[time - 1] = exp_val
    index = larger_index(numbers, exp_val)
    larger = numbers[index:]
    p = 1 - len(larger) / len(numbers)
    if index in suffix:
        suffix_sum = suffix[index]
    else:
        suffix_sum = sum(numbers[index:])
        suffix[index] = suffix_sum
    return suffix_sum / len(numbers) +  exp_val * p


N = int(input())
for n in range(N):
    [count, time] = [int(i) for i in input().split()]
    numbers = [int(i) for i in input().split()]
    d = dict()
    suffix = dict()
    numbers.sort()
    res = dip(numbers, time, d, suffix)
    print("Case #{}: {:.6f}".format(n + 1, res))

Successful Attempt

I found that the range is specified as 0 ≤ K ≤ 5 * 104 , so the number of recursion frames can be as large as this one. If I write this in a recursive way, it is bounded to overflow. So I finally wrote it in an iterative manner.

#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;

int a[20010];
long long sum[20010];
double dp[50010];

int main()
{
    int T, N, K;
    scanf("%d", &T);

    int iCase = 0;
    while(T--) 
    {
        iCase++;
        scanf("%d %d", &N, &K);
        for(int i = 0; i < N; i++)
        {
            scanf("%d", &a[i]);
        }
        sort(a, a+N);
        sum[N] = 0;
        for(int i = N - 1; i >= 0; i--)
        {
            sum[i] = sum[i+1] + a[i];
        }
        dp[0] = (double)sum[0] / N;
        for(int i = 1; i <= K; i++)
        {
            int cnt = upper_bound(a, a+N, dp[i-1]) - a;
            dp[i] = (double)(dp[i - 1] * cnt + sum[cnt]) / N;
        }
        printf("Case #%d: %f\n", iCase, dp[K]);

    }
    return 0;
}