Reputation: 433
We are given an array with n
values.
Example: [1,4,5,6,6]
For each index i
of the array a
,we construct a new element of array b
such that,
b[i]= [a[i]/1] + [a[i+1]/2] + [a[i+2]/3] + ⋯ + [a[n]/(n−i+1)]
where [.]
denotes the greatest integer function.
We are given an integer k
as well.
We have to find the minimum i
such that b[i] ≤ k
.
I know the brute-force O(n^2)
algorithm (to create the array - 'b'), can anybody suggest a better time complexity and way solve it?
For example, for the input [1,2,3]
,k=3
, the output is 1(minimum-index)
.
Here, a[1]=1; a[2]=2; a[3]=3;
Now, b[1] = [a[1]/1] + [a[2]/2] + [a[3]/3] = [1/1] + [2/2] + [3/3] = 3;
b[2] = [a[2]/1] + [a[3]/2] = [2/1] + [3/2] = 3;
b[3] = [a[3]/1] = [3/1] = 3 (obvious)
Now, we have to find the index i
such that b[i]<=k
, k='3'
, also b[1]<=3
, henceforth, 1
is our answer! :-)
Constraints : - Time limits: -(2-seconds) , 1 <= a[i] <= 10^5, 1 <= n <= 10^5, 1 <= k <= 10^9
Upvotes: 4
Views: 604
Reputation: 23955
This probably cannot reach the efficiency of David Eisenstat's answer but since I spent quite a long time figuring out an implementation, I thought I'd leave it up anyway. As it is, it seems about O(n^2)
.
The elements of b[i]
may be out of order, but sections of them are not:
[a[1]/1] + [a[2]/2] + [a[3]/3]
|------ s2_1 -----|
|-s1_1-|
[a[2]/1] + [a[3]/2]
|------ s2_2 -----|
|-s1_2-|
[a[3]/1]
|-s1_3-|
s2_1 < s2_2
s1_1 < s1_2 < s1_3
Binary search for k
on s1
. Any result with an s1_i
greater than k
will rule out a section of ordered rows (rows are b_i
s).
Binary search for k
on s2
on the remaining rows. Any result with an s2_i
greater than k
will rule out a section of ordered rows (rows are b_i
s).
This wouldn't help much since in the worst case, we'd have O(n^2 * log n) complexity, greater than O(n^2).
But we can also search horizontally. If we know that b_i ≤ k
, then it will rule out both all rows with greater or equal length and the need to search smaller s(m)
s, not because smaller s(m)
s cannot produce a sum >= k, but because they will necessarily produce one with a higher i
and we are looking for the minimum i
.
JavaScript code:
var sum_width_iterations = 0
var total_width_summed = 0
var sum_width_cache = {}
function sum_width(A, i, width){
let key = `${i},${width}`
if (sum_width_cache.hasOwnProperty(key))
return sum_width_cache[key]
sum_width_iterations++
total_width_summed += width
let result = 0
for (let j=A.length-width; j<A.length; j++)
result += ~~(A[j] / (j + 1 - i))
return sum_width_cache[key] = result
}
function get_b(A){
let result = []
A.map(function(a, i){
result.push(sum_width(A, i, A.length - i))
})
return result
}
function find_s_greater_than_k(A, width, low, high, k){
let mid = low + ((high - low) >> 1)
let s = sum_width(A, mid, width)
while (low <= high){
mid = low + ((high - low) >> 1)
s = sum_width(A, mid, width)
if (s > k)
high = mid - 1
else
low = mid + 1
}
return [mid, s]
}
function f(A, k, l, r){
let n = A.length
if (l > r){
console.log(`l > r: l, r: ${l}, ${r}`)
return [n + 1, Infinity]
}
let width = n - l
console.log(`\n(call) width, l, r: ${width}, ${l}, ${r}`)
let mid = l + ((r - l) >> 1)
let mid_width = n - mid
console.log(`mid: ${mid}`)
console.log('mid_width: ' + mid_width)
let highest_i = n - mid_width
let [i, s] = find_s_greater_than_k(A, mid_width, 0, highest_i, k)
console.log(`hi_i, s,i,k: ${highest_i}, ${s}, ${i}, ${k}`)
if (mid_width == width)
return [i, s]
// either way we need to look left
// and down
console.log(`calling left`)
let [li, ls] = f(A, k, l, mid - 1)
// if i is the highest, width is
// the width of b_i
console.log(`got left: li, ls, i, high_i: ${li}, ${ls}, ${i}, ${highest_i}`)
if (i == highest_i){
console.log(`i == highest_i, s <= k: ${s <= k}`)
// b_i is small enough
if (s <= k){
if (ls <= k)
return [li, ls]
else
return [i, s]
// b_i is larger than k
} else {
console.log(`b_i > k`)
let [ri, rs] = f(A, k, mid + 1, r)
console.log(`ri, rs: ${ri}, ${rs}`)
if (ls <= k)
return [li, ls]
else if (rs <= k)
return [ri, rs]
else
return [i, s]
}
// i < highest_i
} else {
console.log(`i < highest_i: high_i, i, s, li, ls, mid, mid_width, width, l, r: ${highest_i}, ${i}, ${s}, ${li}, ${ls}, ${mid}, ${mid_width}, ${width}, ${l}, ${r}`)
// get the full sum for this b
let b_i = sum_width(A, i, n - i)
console.log(`b_i: ${b_i}`)
// suffix sum is less than k
// so we cannot rule out either side
if (s < k){
console.log(`s < k`)
let ll = l
let lr = mid - 1
let [lli, lls] = f(A, k, ll, lr)
console.log(`ll, lr, lli, lls: ${ll}, ${lr}, ${lli}, ${lls}`)
// b_i is a match so we don't
// need to look to the right
if (b_i <= k){
console.log(`b_i <= k: i, b_i: ${i}, ${b_i}`)
if (lls <= k)
return [lli, lls]
else
return [i, b_i]
// b_i > k
} else {
console.log(`b_i > k: i, b_i: ${i}, ${b_i}`)
let rl = mid + 1
let rr = r
let [rri, rrs] = f(A, k, rl, rr)
console.log(`rl, rr, rri, rrs: ${rl}, ${rr}, ${rri}, ${rrs}`)
// return the best of right
// and left sections
if (lls <= k)
return [lli, lls]
else if (rrs <= k)
return [rri, rrs]
else
return [i, b_i]
}
// suffix sum is greater than or
// equal to k so we can rule out
// this and all higher rows (`b`s)
// that share this suffix
} else {
console.log(`s >= k`)
let ll = l
// the suffix rules out b_i
// and above
let lr = i - 1
let [lli, lls] = f(A, k, ll, lr)
console.log(`ll, lr, lli, lls: ${ll}, ${lr}, ${lli}, ${lls}`)
let rl = highest_i + 1
let rr = r
let [rri, rrs] = f(A, k, rl, rr)
console.log(`rl, rr, rri, rrs: ${rl}, ${rr}, ${rri}, ${rrs}`)
// return the best of right
// and left sections
if (lls <= k)
return [lli, lls]
else if (rrs <= k)
return [rri, rrs]
else
return [i, b_i]
}
}
}
let lst = [1, 2, 3, 1]
// b [3, 3, 3, 1]
lst = [ 1, 34, 3, 2, 9, 21, 3, 2, 2, 1]
// b [23, 41, 12, 13, 20, 22, 4, 3, 2, 1]
console.log(
JSON.stringify(f(lst, 20, 0, lst.length)))
console.log(`sum_width_iterations: ${sum_width_iterations}`)
console.log(`total_width_summed: ${total_width_summed}`)
Upvotes: 0
Reputation: 65427
Here's an O(n √A)
-time algorithm to compute the b
array where n
is the number of elements in the a
array and A
is the maximum element of the a
array.
This algorithm computes the difference sequence of the b
array (∆b = b[0], b[1] - b[0], b[2] - b[1], ..., b[n-1] - b[n-2]
) and derives b
itself as the cumulative sums. Since the differences are linear, we can start with ∆b = 0, 0, ..., 0
, loop over each element a[i]
, and add the difference sequence for [a[i]], [a[i]/2], [a[i]/3], ...
at the appropriate spot. The key is that this difference sequence is sparse (less than 2√a[i]
elements). For example, for a[i] = 36
,
>>> [36//j for j in range(1,37)]
[36, 18, 12, 9, 7, 6, 5, 4, 4, 3, 3, 3, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
>>> list(map(operator.sub,_,[0]+_[:-1]))
[36, -18, -6, -3, -2, -1, -1, -1, 0, -1, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
We can derive the difference sequence from a subroutine that, given a positive integer r
, returns all maximal pairs of positive integers (p, q)
such that pq ≤ r
.
See complete Python code below.
def maximal_pairs(r):
p = 1
q = r
while p < q:
yield (p, q)
p += 1
q = r // p
while q > 0:
p = r // q
yield (p, q)
q -= 1
def compute_b_fast(a):
n = len(a)
delta_b = [0] * n
for i, ai in enumerate(a):
previous_j = i
for p, q in maximal_pairs(ai):
delta_b[previous_j] += q
j = i + p
if j >= n:
break
delta_b[j] -= q
previous_j = j
for i in range(1, n):
delta_b[i] += delta_b[i - 1]
return delta_b
def compute_b_slow(a):
n = len(a)
b = [0] * n
for i, ai in enumerate(a):
for j in range(n - i):
b[i + j] += ai // (j + 1)
return b
for n in range(1, 100):
print(list(maximal_pairs(n)))
lst = [1, 34, 3, 2, 9, 21, 3, 2, 2, 1]
print(compute_b_fast(lst))
print(compute_b_slow(lst))
Upvotes: 3
Reputation: 1317
Why should calculating b[i]
lead to O(n²)? If i = 1
, it takes n
steps. If i = n
, it takes one step to calculate b[i]...
You could improve your calculation when you abort the sum on the condition Sum > k
.
Let a in N^n
Let k in N
for (i1 := 1; i1 <= n; i1++)
b := 0
for (i2 :=i1; i2 <= n; i2++) // This loop is the calculation of b[i]
b := b + ceil(a[i2]/(i2 + 1))
if (b > k)
break
if (i2 == n)
return i1
Upvotes: -1