Question

I was doing a problem in Scala and this is the summary of the task statement:

There is a list of integers (of length N, 0 < N < 10^5) and another integer S (0 < S < 10^15). You are required to find the minimal size of the minimal subset of the given list of which the sum of elements (in the subset) is greater than or equal to S.

Input is given as below:
4
4 12 8 10
4
4 13 30 100

Output for above example:
1
2
3
-1

First line is length of array, the second is the array of integers (0 < A[i] < 10^9), the third is the number of test cases (0 < T < 10^5) and the fourth contains the S (for each test case).

Here's what I tried: The elements selected do not matter. So I sorted the list of given integers largest first. Then I selected the first one checked if its greater than S. If not, I selected the second element also, and so on until the sum becomes greater than or equal to S.

This algorithm works and I got many test cases correct. But I'm getting Time Limit Exceeded for some. If you can point out how I could make this faster or if there's a better way to do this, it would be much appreciated.

My code (Scala):

object Solution {
  def main(args: Array[String]) {
    val n = readInt()
    val arr: Array[Long] = readLine().split(" ").map(_.toLong).sortWith(_ > _)

    val sums: Array[BigInt] = new Array(n)
    sums(0) = arr(0)
    for(i <- 1 until n) sums(i) = sums(i-1) + arr(i)

    val t = readInt()
    for(i <- 1 to t) {
      val s:BigInt = BigInt(readLong())
      if(sums(n-1) < s)
        println(-1)

      else {
        var i = 0
        while(sums(i) < s) i += 1
        println(i + 1)
      }
    }
  }
}
Was it helpful?

Solution

Making a full list of sums is unnecessary, and using BigInt is wasteful regardless--a Long can hold up to 9.2e18, and you are promised that only 1e15 is going to appear. (In fact, I think 1e15 was chosen so that even a Double can hold the answer without loss of precision.)

Also, you can use a fast sort on primitives rather than a slow generic sort like _ > _ which ends up boxing the integers. So:

val arr = {
  val in = readLine().split(' ').map(_.toInt)
  java.util.Arrays.sort(in)
  in
}

Then, use an accumulator (Long will do):

var sum = 0L

and search for the answer with a while loop, keeping in mind that the biggest elements are last so you want to start at the end:

var i = arr.length-1
while (i >= 0 && sum < t) {
  sum += arr(i)
  i -= 1
}

Now you just need to check if you succeeded or failed before running out of elements:

val answer = {
  if (i < 0) -1
  else arr.length - i - 1
}

OTHER TIPS

Here it is(with O(N) memory used and O(N log N) time used)

scala> import math.{Ordering => Ord}
import math.{Ordering=>Ord}

scala> def search(src: Seq[Long], dst: Seq[Long]): Seq[Int] = {
     |   val cum = src.sorted(Ord.Long.reverse).scanLeft(0L)(_ + _)
     |   dst.map{ v => cum.indexWhere(x => x >= v) }
     | }
search: (src: Seq[Long], dst: Seq[Long])Seq[Int]

scala> search(Seq(4, 12, 8, 10), Seq(4, 13, 30, 100))
res6: Seq[Int] = List(1, 2, 3, -1)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top