How to find the max hotspot of a matrix in python - python

I was given a matrix question on a technical assessment that revolved around the idea of finding the "Max Hotspot".
Areas of connected 1's represent a hotspot, and given a matrix we want to return the max hotspot. Similar to "Number of Islands" or "Word Search.
areamap = [[1, 0, 0, 0],
[1, 1, 1, 0],
[0, 0, 0, 1],
[1, 1, 1, 1]]
areamap = [[0, 0, 0, 0],
[0, 1, 1, 0],
[0, 0, 0, 0],
[1, 0, 0, 0]]
I tried using the 4 way DFS approach but had trouble creating/incrementing a variable that keeps track of the size of each hotspot. Any suggestions/help? Below is my attempt.
The idea of my algo is that every time we find a 1, we "evaporate" it to avoid traveling duplicates. For each 1 we evaporate, we incrementing the count of 1's. The count variable and the tmp list variable always print/return as empty.
class Solution:
def hotSpots(self, area: List[int]):
def evap(r, c, cnt):
if r < 0 or c < 0 or r >= len(area) or c >= len(area[0]) or area[r][c] == "0":
return
cnt += 1
area[r][c] = "0"
evap(r, c + 1)
evap(r, c - 1)
evap(r + 1, c)
evap(r - 1, c)
return
tmp = []
for i in range(len(area)):
for j in range(len(area[0])):
count = 0
if area[i][j] == "1":
evap(i, j, count)
tmp.append(count)
return sum(max(tmp))

There are two issues with the code:
evap does not return any result and so count never gets assigned anything other than 0
You have an array of integers, but you check its elements against strings ("0" and "1")
Solving those issues yields the following code, which outputs the result you want
def hotSpots(area):
def evap(r, c):
if r < 0 or c < 0 or r >= len(area) or c >= len(area[0]) or area[r][c] == 0:
return 0
area[r][c] = 0
return 1 + evap(r, c + 1) + evap(r, c - 1) + evap(r + 1, c) + evap(r - 1, c)
tmp = []
for i in range(len(area)):
for j in range(len(area[0])):
if area[i][j] == 1:
count = evap(i, j)
tmp.append(count)
return sum(max(tmp))

Related

Neater strategy to drop down the numbers in the 2D list

I have a problem. It is a 2D list of non-negative integers will be given like
0, 0, 2, 0, 1
0, 2, 1, 1, 0
3, 0, 2, 1, 0
0, 0, 0, 0, 0
I have to drop the numbers, number columns. e.g. drop down the 1's down 1 column, the 2's down 2 columns, the 3's down 3 columns, and so on. If the number can't be moved down enough, wrap it around the top. (e. g If there is a 3 in the second-to-last row, it should wrap around to the first row.) If two numbers map to the same slot, the biggest number takes that slot.
After this transformation the given matrix above will end up like:
0, 0, 2, 0, 0
3, 0, 0, 0, 1
0, 0, 2, 1, 0
0, 2, 0, 1, 0
Here's my trivial solution to the problem (Assumes a list l is pre-set):
new = [[0] * len(l[0]) for _ in range(len(l))]
idx = sorted([((n + x) % len(l), m, x) for n, y in enumerate(l) for m, x in enumerate(y)], key=lambda e: e[2])
for x, y, z in idx:
new[x][y] = z
print(new)
The strategy is:
Build a list new with 0s of the shape of l
Save the new indices of each number in l and each number as tuple pairs in idx
Sort idx by each number
Assign indices from idx to the respective numbers to new list
Print new
I am not satisfied with this strategy. Is there a neater/better way to do this? I can use numpy.
Let's say you have
a = np.array([
[0,0,2,0,1],
[0,2,1,1,0],
[3,0,2,1,0],
[0,0,0,0,0]])
You can get the locations of the elements with np.where or np.nonzero:
r, c = np.nonzero(a)
And the elements themselves with the index:
v = a[r, c]
Incrementing the row is simple now:
new_r = (r + v) % a.shape[0]
To settle collisions, sort the arrays so that large values come last:
i = v.argsort()
Now you can assign to a fresh matrix of zeros directly:
result = np.zeros_like(a)
result[new_r[i], c[i]] = v[i]
The result is
[[0 0 2 0 0]
[3 0 0 0 1]
[0 0 2 1 0]
[0 2 0 1 0]]
I suggest doing it like this if only because it's more readable :-
L = [[0, 0, 2, 0, 1],
[0, 2, 1, 1, 0],
[3, 0, 2, 1, 0],
[0, 0, 0, 0, 0]]
R = len(L)
NL = [[0]*len(L[0]) for _ in range(R)]
for i, r in enumerate(L):
for j, c in enumerate(r):
_r = (c + i) % R
if c > NL[_r][j]:
NL[_r][j] = c
print(NL)

How to reduce higher values to 1s and sort the ones from right to left

I want to make a python program that quickly reduces a number greater than 1 in an array/list and places it in an empty spot before it. Say we have:
li = [4,1,0,0,0,1,3,0]
we'd get:
rtr = [1,1,0,1,1,1,1,0]
Note how the 4 turns into a 1 because it's already to the left and then the 3 gets divided into 2 positions before the 1 that has already been taken. Can anyone help me with this problem?
You can iterate the list from end to start and keep track of the sum you collect from the values. When you have a non zero sum, take 1 from it to populate the result list, and otherwise put a 0 in the result list.
Here is how that could work:
def spread(lst):
carry = 0
res = []
for i in reversed(lst):
carry += i
res.append(int(carry > 0))
if carry:
carry -= 1
return list(reversed(res))
lst = [4, 1, 0, 0, 0, 1, 3, 0]
print(spread(lst)) # [1, 1, 0, 1, 1, 1, 1, 0]
Using numpy
def fun(l):
s = np.array(l[::-1])
for i in range(len(s)):
if s[i] != 1 and s[i] != 0:
x = s[i+1:]
x[(x == 0).nonzero()[0][:s[i]-1]] = 1
s[i] = 1
return s[::-1].tolist()
print (fun([4,1,0,0,0,1,3,0]))
print (fun([0, 10]))
Output:
[1, 1, 0, 1, 1, 1, 1, 0]
[1, 1]

how to compare 3 list value with If condition

Here are my lists
A = [32,33,34,35,36,37,38,39,40,41,42]
B = [0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0]
C = [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
D = [1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1]
Here, in list B value should be 0, in list C value should be 0 and in list D value should be 1
if this so then it is passed else failed.
As you can see in list B index 2 value is 1 so it is failed like this in list C index 1 value is 1 instead of 0 and in list D index 3,4 value is 2 instead of 1.
I've written the code for this but it is printing multiple time
I want to check the same index value for 3 list
like
B[0] = 0,B[1]=0
C[0] = 0,C[1]=1
D[0] = 1,D[1]=1
as you can see in first iteration values are correct but in 2nd time it get failed in C because 2nd index value is 1 instead of 0. like this way i want to check all 3 list.
for that reason I have taken the index from list A. you can see in my code.
my code-:
comment = "Wrong Signal in "
for first1 in A:
idx_val = A.index(first1)
if (B[idx_val] != 0):
comment = comment + 'B'
if C[idx_val] != 0:
comment = comment + 'C'
if D[idx_val] != 1:
comment = comment + 'D'
print comment
Output-'Wrong Signal in B,C,D,D,B
But i'm expecting the output like this
Output- 'Wrong Signal in B,C,D
and another example if B list's all value are zeros and C,D has wrong value in any index then it should print
output -'Wrong Signal in C,D
Thank You In Advance
for a, b, c, d in zip(A, B, C, D):
r = [
key for key, value, check in zip('bcd', (b, c, d), (0, 0, 1))
if value != check
]
if r:
print(f"{a}: wrong signal for {', '.join(r)}")
you can cast your lists to set and compare against one value:
def get_bad_list(lists, expected_values):
bad_lists = ''
for l, (s, v) in zip(lists, expected_values):
if set(l) != v:
bad_lists += s
return bad_lists
bad_lists = get_bad_list([B, C, D], [('B', {0}), ('C', {0}), ('D', {1})])
if bad_lists:
print 'Wrong Signal in ' + ', '.join(bad_lists) + '!'
else:
print 'All good!'
output:
Wrong Signal in B, C, D!
if you want to iterate over the lists and "to check the same index value" you can use:
comment = "Wrong Signal in "
is_B_good = True
is_C_good = True
is_D_good = True
for index in range(len(A)):
if is_B_good and B[index] != 0:
is_B_good = False
comment = comment + 'B '
if is_C_good and C[index] != 0:
is_C_good = False
comment = comment + 'C '
if is_D_good and D[index] != 1:
is_D_good = False
comment = comment + 'D '
print comment
output:
Wrong Signal in D C B
You need to exit your loop after you found the first instance. So you have to check if 0 is in B EVEN JUST ONCE. You can simply ask "if 0 in B". Then you can already quit, because you don't want to know how many times 0 is in B.
A = [32,33,34,35,36,37,38,39,40,41,42]
B = [0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0]
C = [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0]
D = [1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1]
comment = "Wrong Signal in "
def check_if_fine(List,wrong_numbers,List_name):
No_repetitions = set(List)
for number in wrong_numbers:
if number not in No_repetitions:
return ''
else:
return List_name
comment += check_if_fine(B,[1],'B')
comment += check_if_fine(C,[1],'C')
comment += check_if_fine(D,[2],'D')
print(comment)

How to count specific elements before an element in a list?

We have a list
list = [1, 1, 1, 0, 1, 0, 0, 1]
I am trying find a function that would count the number of 0's before each item and then multiply this number by 3.
def formula(n):
for x in list:
if x == 1:
form = n * 3
return form
#If x is 1, count the number of zeros right before x and multiply this by 3,
For example for the list above, the first element is a 1 and there are no numbers right before it, the program should compute 0 * 3 = 0, for the second item, which is also a 1, the number right before it is also not a zero, the program should also compute 0 * 3 = 0. The 4th element is 0 so the program should ignore, For the 5th element which is a 1, the number right before it is a 0, the programme to compute 1 * 3 = 3, for the 6th element the number right before it is a 1, the system should compute 0 * 3 = 0. The 7th element is a 0, since x is not equal to 1 the program should not do anything. For the last element which is a 1, the last two numbers before it are zeros, the program should compute 2 * 3 = 6
I believe you are looking for a generator, with a simple counter:
def get_values (list):
n = 0
for x in list:
if x == 1:
yield n * 3
n = 0 # reset the counter
elif x == 0:
n += 1 # increment the counter
print(list(get_values([1, 1, 1, 0, 1, 0, 0, 1])))
# [0, 0, 0, 3, 6]
Try this,
def formula(l):
count_zero = 0
result = []
for i in l:
if i == 1:
result.append(3*count_zero)
count_zero = 0
elif i == 0:
count_zero += 1
return result
# Test case
l = [1, 1, 1, 0, 1, 0, 0, 1]
result = formula(l)
print(result)
# [0, 0, 0, 3, 6]
Here is my solution for the problem.
test_list = [1, 1, 1, 0, 1, 0, 0, 1]
def formula(list):
track = []
start = 0
for i,n in enumerate(list):
count_list_chunk = list[start:i]
if count_list_chunk.count(0) > 0 and n != 0:
start = i
if n != 0:
track.append( count_list_chunk.count(0)*3 )
return track
print formula(test_list)
#[ 0, 0, 0, 3, 6]

Unable to increment array elements correctly for "Minesweeper"

I'm starting to learn Python, I just started with a simple example. The question was to count mines near each place in a table. Consider input file below:
4 4
*...
....
.*..
....
3 5
**...
.....
.*...
0 0
The output should be like
Field #1:
*100
2210
1*10
1110
Field #2:
**100
33200
1*100
And my code is:
#!/usr/bin/python
import pprint
fin = open("1.2.in")
fout = open("1.2.out")
while True:
i, j = [int(x) for x in fin.readline().split()]
if(i == 0):
break
arr = []
for k in range(0,i):
line = fin.readline();
arr.append(list(line))
pprint.pprint(arr)
resarr = [[0]*j]*i
for row in range(0,i):
for col in range(0,j):
for rowIndex in range(-1,2):
for colIndex in range(-1,2):
# print row,rowIndex, col,colIndex
if (row + rowIndex < i) and (row + rowIndex >= 0) and ( col + colIndex < j) and (col+colIndex >=0) and (rowIndex != 0 or colIndex != 0):
#pprint.pprint(resarr[row][col])
#if arr[row+rowIndex][col+colIndex] == "*":
#print row,rowIndex, col,colIndex, " ", arr[row+rowIndex][col+colIndex]
#print resarr[row][col]
resarr[row][col] += 1
#pprint.pprint(resarr)
# print col+colIndex
print i,j
pprint.pprint(resarr)
I don't know what's wrong, but when I want to increment resarr, a total column is incremented.
Your problem is
resarr = [[0]*j]*i
This means: Take i references to the same list defined by [0]*j and create a list of those.
The thing you want is:
resarr = [[0]*j for _ in range(i)]
This creates a new list ([0, 0, ...]) i times instead.
See this:
>>> a = [0] * 4
>>> a
[0, 0, 0, 0]
>>> b = [a] * 4
>>> b
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
>>> id(b[0]) # get the "address" of b[0]
42848200
>>> id(b[1]) # b[1] is the same object!
42848200
>>> b[0][0] = 1
>>> b
[[1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0], [1, 0, 0, 0]]

Categories