import java.util.* /** * Use dynamic programming. * dp[i][j] is the minimum cost to serve the first j customers with i access points. * The optimal position for 1 access point to serve a range of customers will be * their average. This can be derived by writing the cost for placing access point * at position x as * f(x) = (x-x_1)^2 + (x-x_2)^2 + ... + (x - x_p)^2 * Then f'(x) = 2*p*x - 2*sum_{i=1}^{p}(x_i) * This is equal to 0 when x=average(x_i) * Use prefix sums and prefix sum of squares to efficiently compute the cost * for covering a range of customers. * Complexity: O(k*(m+n)^2) * * @author Finn Lidbetter */ fun main(args: Array) { var tokens = readLine()!!.split(" ") val m = tokens[0].toInt() val n = tokens[1].toInt() val k = tokens[2].toInt() val s = tokens[3].toInt() val roadWidthCost = (s/2.0) * (s/2.0) var customers = DoubleArray(m + n) tokens = readLine()!!.split(" ") for ((index, token) in tokens.withIndex()) { customers[index] = token.toDouble() } tokens = readLine()!!.split(" ") for ((index, token) in tokens.withIndex()) { customers[index + m] = token.toDouble() } customers.sort() /* val prefixSum = customers.runningFold(0.0) { sum, item -> sum + item } val sqPrefixSum = customers.runningFold(0.0) { sum, item -> sum + item * item } */ var prefixSum = Array(customers.size + 1) { 0.0 } var currSum = 0.0 for (index in 0..customers.size - 1) { prefixSum[index+1] = currSum + customers[index] currSum = prefixSum[index+1] } var sqPrefixSum = Array(customers.size + 1) { 0.0 } currSum = 0.0 for (index in 0..customers.size - 1) { sqPrefixSum[index+1] = currSum + customers[index] * customers[index] currSum = sqPrefixSum[index+1] } // dp[i][j] is the minimum cost to serve the first j customers using i access points. var dp = Array(k+1) { DoubleArray(customers.size + 1) } for (row in dp) { Arrays.fill(row, Double.POSITIVE_INFINITY) } dp[0][0] = 0.0 for (numAccessPoints in 1..k) { for (currLastCustomer in 1..customers.size) { for (prevLastCustomer in 0..currLastCustomer) { val numNewCoveredCustomers = currLastCustomer - prevLastCustomer val newCoveredRangeSum = prefixSum[currLastCustomer] - prefixSum[prevLastCustomer] val bestPosition = newCoveredRangeSum / numNewCoveredCustomers val xSqCoeff = numNewCoveredCustomers val xCoeff = -2 * newCoveredRangeSum val constant = sqPrefixSum[currLastCustomer] - sqPrefixSum[prevLastCustomer] val cost = ( xSqCoeff * bestPosition * bestPosition + xCoeff * bestPosition + constant + numNewCoveredCustomers * roadWidthCost ) if (dp[numAccessPoints - 1][prevLastCustomer] + cost < dp[numAccessPoints][currLastCustomer]) { dp[numAccessPoints][currLastCustomer] = dp[numAccessPoints - 1][prevLastCustomer] + cost } } } } println(dp[k][customers.size]) }