I'm currently trying to solve a problem on codechef called "Snake Eating". https://www.codechef.com/SDPCB21/problems/SNAKEEAT . Here is the solution that I came up with
try:
T = int(input())
for t in range(T):
N, Q = tuple(input().split())
N, Q = int(N), int(Q)
L = list(input().split())
for i in range(len(L)):
L[i] = int(L[i])
K = []
for i in range(Q):
K.append(input())
for i in range(len(K)):
K[i] = int(K[i])
for q in range(Q):
def solution(N, Q, K, L):
x = K[q]
L_temp = list(L)
count = 0
for element in L_temp:
if element >= x:
L_temp.remove(element)
count += 1
L_temp.sort()
while not (len(L_temp) - 1 < x - L_temp[-1]):
for i in range(x - L_temp[-1]):
L_temp.pop(0)
L_temp.pop(-1)
count += 1
return count
print(solution(N, Q, K, L))
except:
pass
When I submitted my code. It shows time limit exceeded. Can someone tell me how can I optimize my code to achieve under 10 seconds runtime?
This is contains some things that could be done better.
def solution(N, Q, K, L):
x = K[q]
L_temp = list(L)
count = 0
for element in L_temp:
if element >= x:
L_temp.remove(element)
count += 1
L_temp.sort()
while not (len(L_temp) - 1 < x - L_temp[-1]):
for i in range(x - L_temp[-1]):
L_temp.pop(0)
L_temp.pop(-1)
count += 1
return count
This loop is quadratic:
for element in L_temp:
if element >= x:
L_temp.remove(element)
count += 1
As we are making a copy of a list and then counting the items that are greater than x. One can do that in linear time
L_temp = [element for element in L if element < x]
count = len(L) - len(L_temp)
This loop:
while not (len(L_temp) - 1 < x - L_temp[-1]):
for i in range(x - L_temp[-1]):
L_temp.pop(0)
L_temp.pop(-1)
count += 1
Doesn't actually need to mutate L_temp. It can operate instead on two indexes i and j.
i = 0
j = len(L_temp) - 1
while not (j - i) < x - L_temp[j]:
i += x - L_temp[j] - 1
j -= 1
count += 1
There might be a min(0, ) call on the line that adds to i.
Related
Im not very sure if I will translate the assignment correctly, but the bottom line is that I need to consider the function f(i) = min dist(i, S), where S is number set of length k and dist(i, S) = sum(j <= S)(a (index i) - a(index j)), where a integer array.
I wrote the following code to accomplish this task:
n, k = (map(int, input().split()))
arr = list(map(int, input().split()))
sorted_arr = arr.copy()
sorted_arr.sort()
dists = []
returned = []
ss = 0
indexed = []
pop1 = None
pop2 = None
for i in arr:
index = sorted_arr.index(i)
index += indexed.count(i)
indexed.append(i)
dists = []
if (index == 0):
ss = sorted_arr[1:k+1]
elif (index == len(arr) - 1):
sorted_arr.reverse()
ss = sorted_arr[1:k+1]
else:
if index - k < 0:
pop1 = 0
elif index + k > n - 1:
pop2 = None
else:
pop1 = index - k
pop2 = index + k + 1
ss = sorted_arr[pop1:index] + sorted_arr[index + 1: pop2]
for ind in ss:
dists.append(int(abs(i - ind)))
dists.sort()
returned.append(str(sum(dists[:k])))
print(" ".join(returned))
But I need to speed up its execution time significantly.
I need to implement Merge Sort using Python 3. I coded it. But It doesn't give proper output. Can anybody check it please?
Here my code is,
def mergeSort(A, p, r):
if p < r:
q = (p + r) // 2
mergeSort(A, p, q)
mergeSort(A, q+1, r)
Merge(A, p, q, r)
def Merge(A, p, q, r):
i = 1
j = q+1
k = 0
TEMP = [0] * (r+1)
while i <= q and j <= r:
if A[i] <= A[j]:
TEMP[k] = A[i]
k += 1
i += 1
else:
TEMP[k] = A[j]
k += 1
j += 1
if (j > r) :
for t in range(0, q-1):
A[r-t] = A[q-t]
for t in range(0, k-1):
A[p+t] = TEMP[t+1]
A = [15, 16, 13, 10, 19, 18]
mergeSort(A, 0, len(A)-1)
print(A)
Thank you
The way you perform merge looks weird (to me), but I will correct on what you have so far.
1- Initialization value of i is wrong, it should be:
i = p
because i is the first element you will look in array A.
2- Initialization value of size of TEMP array is wrong, it should be:
(r - p + 1)
3- There seems a mistake in filling in TEMP array and/or replacing A array elements, here is the fixed code. I wrote a comment about the part after first while loop to indicate what needs to be done at that point.
def mergeSort(A,p,r):
if p < r:
q = (p+r)//2
mergeSort(A,p,q)
mergeSort(A,q+1,r)
Merge(A,p,q,r)
def Merge(A,p,q,r):
i = p
j = q+1
k=0
TEMP = [0]*(r - p + 1)
while i <= q and j <= r:
if A[i] <= A[j]:
TEMP[k] = A[i]
k += 1
i += 1
else:
TEMP[k] = A[j]
k += 1
j += 1
"""
There are currently 2 cases
1- i > q, means we exhausted left but there are elements in the right
2- j > r, means we exhausted right but there are elements in the left
"""
if (j > r):
# copy elements at the left side to temp
while (i <= q):
TEMP[k] = A[i]
i += 1
k += 1
else:
# copy elements at the right side to temp
while (j <= r):
TEMP[k] = A[j]
j += 1
k += 1
# replace elements in A with elements in TEMP
for t in range(k):
A[p+t] = TEMP[t]
A = [15,16,13,10,19,18]
mergeSort(A,0,len(A)-1)
print(A)
The error lies in the Merge() function.
Initialisation of i=p and not i=1
After the while loop terminates, there's a chance that either i<q or j<r. We need to accommodate those cases as well.
Size of array TEMP was incorrect.
Corrected Merge Function:
def Merge(A,p,q,r):
i = p
j = q+1
k=0
TEMP = [0]*(r-p+1)
while i <= q and j <= r:
if A[i] <= A[j]:
TEMP[k] = A[i]
k += 1
i += 1
else:
TEMP[k] = A[j]
k += 1
j += 1
while i<=q:
TEMP[k] = A[i]
k+=1
i += 1
while j<=r:
TEMP[k] = A[j]
k+=1
j += 1
for t in range (p,r+1):
A[t] = TEMP[t-p]
Note: Please try using more meaningful variable names.
I am doing this exercise:
You are given an array and you need to find number of triplets of indices i, j, and k, such that the elements at those indices are d[i] < d[j] < d[k] and d[i] + d[j] + d[k] <= t, with t and d given by user
I have the following code where it tries:
def test(t, d):
d = sorted(d)
total = 0
size = len(d)
for x in range(size - 2):
for i in range(x+1, size - 1):
for j in range(i+1, size):
if (d[x] + d[i] + d[j]) <= t:
total += 1
return total
The code works fine but when it comes to bigger cases, the code isn't optimized enough that it can compute it in less than 10 seconds. So I changed it up to this instead:
def test(t, d):
d = sorted(d)
total = 0
size = len(d)
for i in range(size - 2):
j = i +1
k = size - 1
while k > j:
if (d[i] + d[j] + d[k]) <= t:
total = k - j + total
j += 1
else:
k -= 1
return total
Sample case:
t = 8
d = [1, 2, 3, 4, 6]
Output: 3
This code works better compared to before but it is still not optimized enough. Is there something I could do or use to improve the optimization.
I can see another optimization to #paxdiablo's answer. You can stop the loop you're in completely as soon as you go over t, rather than just not doing the body of the remaining iterations of that loop:
def test(t, d):
d = sorted(d)
total = 0
size = len(d)
for x in range(size - 2):
if d[x] > t:
break
for i in range(x+1, size - 1):
if d[x] + d[i] > t:
break
for j in range(i+1, size):
if (d[x] + d[i] + d[j]) > t:
break
total += 1
return total
So it looks like you're trying to find the count of all triplets where the sum is less than or equal to a specific value (now confirmed with your question update).
If that's the case and, since you're sorting the data, you can optimise the non-inner loops to remove checking for impossible situations.
For example, if d[x] is already greater than t, there's no point in running the two non-outer loops for that, since everything after that is greater than d[x].
Ditto for d[x] + d[i] being greater than t, there's no point running the inner loop. In other words, something like this could be an optimisation:
def test(t, d):
d = sorted(d)
total = 0
size = len(d)
for x in range(size - 2):
if d[x] <= t: # Check need for two inner loops.
for i in range(x + 1, size - 1):
if d[x] + d[i] <= t: # Check need for inner loop.
for j in range(i + 1, size):
if d[x] + d[i] + d[j] <= t:
total += 1
return total
Although the problem with that is that it still carries on the current loop, just ignoring the loops inside that one. It's an even better option to stop the current loop completely since no future iteration of it will deliver useful results:
def test(t, d):
d = sorted(d)
total = 0
size = len(d)
for x in range(size - 2):
if d[x] > t: # Exit loop on impossibility.
break
for i in range(x + 1, size - 1):
if d[x] + d[i] > t: # Ditto.
break
for j in range(i + 1, size):
if d[x] + d[i] + d[j] > t: # Ditto.
break
total += 1
return total
I want to count how many inversions there are in a list while sorting the list using mergesort. This is my code so far where 'x' counts the ammount of inversions while the rest sorts it:
import sys
x = 0
def merge_sort(A):
merge_sort2(A, 0, len(A) - 1)
def merge_sort2(A, first, last):
if first < last:
middle = (first + last) // 2
merge_sort2(A, first, middle)
merge_sort2(A, middle + 1, last)
merge(A, first, middle, last)
def merge(A, first, middle, last):
global x
L = A[first:middle + 1]
R = A[middle + 1:last + 1]
L.append(sys.maxsize)
R.append(sys.maxsize)
i = j = 0
for k in range(first, last + 1):
if L[i] <= R[j]:
A[k] = L[i]
i += 1
else:
A[k] = R[j]
j += 1
x += 1
x += len(L[first + 1:])
When I call merge sort using a list, the variable x is support to give the amount of inversions in the list. So If the list was '[4,3,2,1], x would be 6. If the list was [1,2,3] x would be 0. I change the value of x whenever the right is greater than the left in the merge definition however, the number always gets way too big. What am I doing wrong?
Check my work but, I think instead of:
x += 1
x += len(L[first + 1:])
you want:
x += middle + 1 + j - k
basically, you want to add the difference between where item k is actually coming from, and where you'd expect it to come from if everything was already sorted.
Your merge step is a little hard for me to understand — I'm not sure why you are doing this (maybe just another way to merge?):
L.append(sys.maxsize)
R.append(sys.maxsize)
but I couldn't get everything to work out with the extra elements added to the partitions. And I think you end up counting the extra element in L as an inversion with each merge move from R
I think that's causing some of the problems. But you also have two other issues:
Your last line isn't quite the right logic:
x += len(L[first + 1:])
the number of inversions will the number of elements in L that you jump over. You're counting almost every element of L each time. Something like this works better:
x += len(L[i:])
and then at the end, you may have elements left over whose inversions you haven't counted yet. Maybe that's not an issue with your extra elements but in a more traditional merge it is. Here's the way I would count the inversions:
def merge(A, first, middle, last):
global x
L = A[first:middle+1]
R = A[middle+1:last+1]
i = j = 0
k = first
print(L, R)
while i<len(L) and j<len(R):
if L[i] <= R[j]:
A[k] = L[i]
i += 1
else:
A[k] = R[j]
j += 1
# count how many left in L
x += len(L[i:])
k += 1
# take care of any leftovers in L or R
while i < len(L):
A[k] = L[i]
i += 1
k+=1
while j < len(R):
A[k] = R[j]
j += 1
k+=1
x += len(L[i:])
So I have this code in python and currently it only returns the maximum value for cutting a rod. How can I modify this to also give me where the cuts were made? It takes a list of prices whose indices+1 correspond to the value of the rod at each length, and n, for length of the rod.
the problem:http://www.radford.edu/~nokie/classes/360/dp-rod-cutting.html
def cutRod(price, n):
val = [0 for x in range(n+1)]
val[0] = 0
for i in range(1, n+1):
max_val = 0
for j in range(i):
max_val = max(max_val, price[j] + val[i-j-1])
val[i] = max_val
return val[n]
If this is the question : Rod cutting
Assuming code works fine, You will have to add a condition instead of Max operation to check which of two was picked and push that one in an array :
def cutRod(price, n):
val = [0 for x in range(n+1)]
val[0] = 0
output = list()
for i in range(1, n+1):
max_val = 0
cur_max_index = -1
for j in range(i):
cur_val = price[j] + val[i-j-1]
if(cur_val>max_val):
max_val = cur_val #store current max
cur_max_index = j #and index
if cur_max_index != -1:
output.append(cur_max_index) #append in output index list
val[i] = max_val
print(output) #print array
return val[n]
I know this is old but just in case someone else has a look...I was actually just looking at this problem. I think the issue is here that these dp problems can be tricky when handling indices. The previous answer is not going to print the solution correctly simply because this line needs to be adjusted...
cur_max_index = j which should be cur_max_index = j + 1
The rest...
def cut_rod(prices, length):
values = [0] * (length + 1)
cuts = [-1] * (length + 1)
max_val = -1
for i in range(1, length + 1):
for j in range(i):
temp = prices[j] + values[i - j - 1]
if temp > max_val:
max_val = prices[j] + values[i - j - 1]
cuts[i] = j + 1
values[i] = max_val
return values[length], cuts
def print_cuts(cuts, length):
while length > 0:
print(cuts[length], end=" ")
length -= cuts[length]
max_value, cuts = cut_rod(prices, length)
print(max_value)
print_cuts(cuts, length)
Well, if you need to get the actual pieces that would be the result of this process then you'd probably need a recursion.
For example something like that:
def cutRod(price, n):
val = [0 for x in range(n + 1)]
pieces = [[0, 0]]
val[0] = 0
for i in range(1, n + 1):
max_val = 0
max_pieces = [0, 0]
for j in range(i):
curr_val = price[j] + val[i - j - 1]
if curr_val > max_val:
max_val = curr_val
max_pieces = [j + 1, i - j - 1]
pieces.append(max_pieces)
val[i] = max_val
arr = []
def f(left, right):
if right == 0:
arr.append(left)
return
f(pieces[left][0], pieces[left][1])
f(pieces[right][0], pieces[right][1])
f(pieces[n][0], pieces[n][1])
return val[n], arr
In this code, there is an additional array for pieces which represents the best way to divide our Rod with some length.
Besides, there is a function f that goes through all pieces and figures out the optimal way to divide the whole Rod.