Code organisation
You've separated the input/output from the actual algorithm and that's a very nice thing that is not done as often as it should be in questions tagged with programming-challenge
.
The only thing I'd change about the way you've done this is that the min_bread
function does not need to be provided the N
value as it should correspond to the length of the breads
argument.
Loop like a native
I highly recommand Ned Batchelder's excellent talk called "Loop Like A Native". Among other things, it tells you that in general, you should not use a combination of range
(or xrange
) and len
to iterate over something.
At this stage, the code looks like:
def min_bread(breads):
oddcount = 0
oddindices = []
for i, val in enumerate(breads):
if val % 2:
oddcount += 1
oddindices.append(i)
# Odd number of odd loaves make it impossible to distribute
if oddcount % 2:
return "NO"
# All are odd (return that number) or even (return 0)
if oddcount==len(breads) or oddcount == 0:
return oddcount
loaves = 0
for idx in xrange(0, oddcount, 2):
loaves += (oddindices[idx+1] - oddindices[idx])*2
return loaves
Details
You don't need to compute oddcount
as you build oddindices
, you can simply do oddcount = len(oddindices)
after the loop.
Also, now you have a nice way to initialise oddindices
. Indeed, in Python, the pattern l = []; for i in iter: if filter(i): l.append(func(i))
can be rewritten using a structure called "list comprehension" to get : l = [func(i) for i in iter if filter(i)]
.
In your case, you get:
oddindices = [i for i, val in enumerate(breads) if val % 2]
oddcount = len(oddindices)
Multiply once
You multiply by 2 each value you add to loaves
. You could do it only once at the very end.
Sum builtin
You could use the sum
builtin to perform the sums efficiently.
You'd get something like:
return 2 * sum(oddindices[idx+1] - oddindices[idx] for idx in xrange(0, oddcount, 2))
Then, the whole code looks like:
def min_bread(breads):
oddindices = [i for i, val in enumerate(breads) if val % 2]
oddcount = len(oddindices)
# Odd number of odd loaves make it impossible to distribute
if oddcount % 2:
return "NO"
# All are odd (return that number) or even (return 0)
if oddcount==len(breads) or oddcount == 0:
return oddcount
return 2 * sum(oddindices[idx+1] - oddindices[idx] for idx in xrange(0, oddcount, 2))
Avoiding the additional array
A different way to write this without an additional array is to see that the different indices of odd values are substracted, then added, then substracted, etc. Also, you have to perform as many additions as substractions. We could use a simple flag to handle both the alternance of sign and the check that we've performed the same number of additions and substractions. For instance (highly not tested):
def min_bread(breads):
sign = -1
loaves = 0
for i, val in enumerate(breads):
if val % 2:
loaves += sign * i
sign *= -1
return "NO" if sign == 1 else 2 * loaves