Warning
This page is located in archive. Go to the latest version of this course pages.

Homework 1 hints

'''
 
The very basic solution
 
 
1.
Read the problem description and make sure you understand it.
Often, it is quite impossible to start solving a problem without its proper
understanding.
 
The test of understanding is simple:
Go through the data in the given Examples 1, 2 and 3. These data are small
and the problem in each three cases can be solved quickly with just pen and paper.
Perform manually all necessary calculations to solve the problem
in all three cases.
Each time your calculation should yield exactly the result given in
the respective examples.
 
If your results, even after repetitive attempts, differ from the results in
the examples, it is an indication that you probably miss  or
interpret differently some idea(s) in the problem description.
In such case, discuss your troubles with your colleagues, classmates
or with the teacher, before you move on.
 
If you still experience difficulties with understanding, do the following:
Read the problem description from the very beginning, sentence by sentence.
Do not skip any paragraph, any sentence and even any symbol or any
formula. Mark the first place in the text where you stopped
understanding the problem. Point out this place to your classmate, colleague
or a teacher  and ask for help/consultations regarding this particular segment of the text.
 
Repeat all above actions, if necessary, to get full understanding of the problem.
 
 
 
2.
When you are quite confident that your understanding of the
problem description is correct and satisfactory
start thinking about the implementation.
If it is more or less obvious to you at this point
how to write the solution code,
what function or data structures you might use,
what will be the overall architecture of your solution,
then this help is not intended for you and you may start
coding the solution right away.
 
Here we present a suggestion of a few steps which might lead you to a successful solution.
Other authors might suggest different steps, different approaches.
 
 
A.
Write a solution of a very simplified or extremely simplified original problem.
When this solution works correctly expand it so that it is capable
of processing little bit more complex versions of the problem.
 
A1.
Imagine there is only one robot in the whole problem
Design a function which simulates one move of the robot.
It may look like the following:
 
def oneMoveTime( T, startPos, moveDir, moveLen )
   #1. calculate, based on values of startPos, moveDir, moveLen,
   # where is the end position of the move
   endPos = # your calculation here (it may take more than one statement)
   # 2. produce the sum of all elements in T between
   # startPos and endPos, including startPos and endPos.
   sumOfTimes = # your calculation here
   # 3. return the calculated value
   return sumOfTimes
 
A2.
Check the function oneMoveTime
Create a few tiny examples which can be easily processed by hand
without the help of the computer. Check manually if function oneMoveTime
processes these examples correctly. Try to make those tiny examples
as nasty and unpleasant as possible, the function has to process them correctly.
 
For example:
T = [1,2,3,2,1]
startPos = 1; moveDir = 1; moveLen = 6
print( oneMoveTime(T, startPos, moveDir, moveLen) )
The output should be 8.
 
A3.
Create at least five or six more additional examples like this one,
with different values in T, and in the remaining variables.
Each time the output should be correct.
If it is not correct, insert  tracing prints in the function oneMove
(e.g. print(endPos) and more like that) to see what is going on in the function during 
its execution. This strategy often helps to locate the source of the error.
 
B.
Expand the solution represented by the function oneMove.
Imagine that there is one additional robot. This robot never moves, it stays all the time on the same
place and thus its sphere of influence never changes. Let us call this robot robot2 
and let the original robot be called robot1.
The (permanent) position of robot2 might be an additional parameter of the function oneMoveTime.
 
To solve the extended problem we have to be able to find the portion of the corridor
where robot1 moves through the sphere of influence of robot2.
We might think of this subproblem as a problem of determining the intersection of two
intervals on an usual number axis.
So, before we continue expanding function oneMoveTime, let us digress for a moment into
the intersection subproblem.
 
Robot1 in the corridor moves in the sections which form an interval which one end is 
at the start position of the move and the other end is at the end position of the move. 
This interval might be specified by a pair of numbers [LeftEnd1, RightEnd1]. The value of 
LeftEnd1 is equal to the smaller of the two values (start position, end position), 
the value of RightEnd1 is equal to the bigger of the two values
(start position, end position). For example, if the start position is 8 
and the end position is 5 (robot1 moves to the left),
 the move interval is specified by the pair [5,8].
 
 The sphere of influence of robot2 can be also interpreted as an interval made of sectors.
It can be specified by a pair of numbers [LeftEnd2, RightEnd2] where the value of LeftEnd2is
the index of the leftmost sector in the sphere of influence of robot2 and the value of  and
 the value of RightEnd2  is the index of the rightmost sector in the same sphere of influence.
 
The critical observation which brings us closer to the final solution is that robot1,
during its move, slows down exactly when it is operating in the *intersection*
of the two intervals [LeftEnd1, RightEnd1] and [LeftEnd2, RightEnd2].
At this point, if you are getting lost, draw yourself a picture or use/modify
the picture from the problem description. Study the picture and/or redraw it more times
until you completely absorb this fact which is repeated here once more:
"robot1, during its move, slows down exactly when it is operating in the intersection
of the two intervals [LeftEnd1, RightEnd1] and [LeftEnd2, RightEnd2]"
 
Equipped with the new information we can now add a piece of code to the function oneMove
 
def oneMoveTime( T, startPos, moveDir, moveLen, posRobot2, R )
   # 1. and 2. are same as before:
   #1. calculate, based on values of startPos, moveDir, moveLen,
   # where is the end position of the move
   endPos = # your calculation here (it may take more than one statement)
   # 2. produce the sum of all elements in T between
   # startPos and endPos, including startPos and endPos.
   sumOfTimes = # your calculation here
 
   # new stuff
   # 3a. set [LeftEnd1, RightEnd1] to be [startPos, endPos]
   # 3b. calculate the pair [LeftEnd2, RightEnd2]
   # 3c. calculate the intersection of [LeftEnd1, RightEnd1] and [LeftEnd2, RightEnd2]
   # 3d. add the sum of all visit times in the intersection to sumOfTimes
 
   # 4. return the calculated value
   return sumOfTimes
 
Python does not contain a built-in function of calculating the intersection of two intervals,
you hove to write one yourself, the input parameters will be the four numbers specifying
the two intervals, tha output parameters should be two numbers specifying,
in this order, the index of the left end and the index of the right end of the intersection.
When the intersection is empty the returned values should indicate it, you may, for example, 
return negative values in such case or throw an exception, etc., 
depending on your personal preferences.
 
 
good practice -- avoid multiple copies of the same data, same information stored
(or rather scattred accros) at different places in the code. Such multiple
copies lead to confusion on the part of the programmer and also they tend to slow
down the program execution.

Example implementation

def rangeSingleTime( i_left, i_right ):
    # calculate time spent in the segment [i_left, i_right]
    # as if the other two robots do not exist
    if i_left > i_right: return 0
    if i_left == 0:      return prefsum[i_right]
    else:                return prefsum[i_right] - prefsum[i_left-1]
 
 
def rangeFullTime( i_left, i_right, pos2, pos3 ):
    # -- calculate time a robot spends in the segment [i_left, i_right]
    #    taking in account the influence of the other two robots
    # -- i_left, i_right define the boundaries of the section
    #    in which the move of current robot is performed
    # --  pos2 <= pos3 are current positions of the other two robots,
    #     whichever physical pair of robots it may be
 
    # start with single time in segment [i_left, i_right]
    fullTime = rangeSingleTime(i_left, i_right )
 
    # define left and right boundaries of the other two robots influences
    # first, swap two remaining robots so that pos2 <= pos3, if necessary
    if pos2 > pos3:   
        pos2, pos3 = pos3, pos2
    min2 = max(0, pos2-D )
    max2 = min( pos2+D, L-1 )
    min3 = max(0, pos3-D )
    max3 = min( pos3+D, L-1 )
 
    # additional time spent in sphere(s) of influence of robots 2 and 3
    if max2 < min3-1 : # those spheres are disjoint:
        fullTime += rangeSingleTime( max(i_left, min2), min(max2, i_right) )
        fullTime += rangeSingleTime( max(i_left, min3), min(max3, i_right) )
    else:  # spheres overlap
        fullTime += rangeSingleTime( max(i_left, min2), min(max3, i_right) )
 
    return fullTime
 
 
def oneMove(pos1, pos2, pos3, moveLen, dir):
    # define the leftmost, the rightmost and the end position
    # of the move
    if dir == 0:
        leftpos = max(0, pos1-moveLen)
        rightpos = pos1
        endpos = leftpos
    if dir == 1:
        leftpos = pos1
        rightpos = min(pos1+moveLen, L-1)
        endpos = rightpos
 
    return rangeFullTime( leftpos, rightpos, pos2, pos3 ), endpos
 
 
# --------------------------------------------------------------------------------------------
#                    M A I N
# --------------------------------------------------------------------------------------------
 
# read initial configuration
L, D, posR1, posR2, posR3 = map(int, input().split())
T = [int(i) for i in input().split()] # len(T) == L
 
# precompute prefix sum
prefsum = [0] * L
prefsum[0] = T[0];
for i in range(1, L):
    prefsum[i] = prefsum[i-1] + T[i]
 
# Number of move commands
C = int(input())
 
# perform separate move commands
totaltime = 0
for ic in range(C):
    iRobot, dir, moveLen = map(int, input().split())
    moveLen -= 1 # adjust move length, just technicality
 
    # the moving robot is always the first in the list of parameters
    if iRobot == 1:
        movetime, posR1 = oneMove(posR1, posR2, posR3, moveLen, dir)
    if iRobot == 2:
        movetime, posR2 = oneMove(posR2, posR1, posR3, moveLen, dir)
    if iRobot == 3:
        movetime, posR3 = oneMove(posR3, posR1, posR2, moveLen, dir)
    totaltime += movetime
 
print( totaltime )

courses/be5b33pge/practices/homework1.txt · Last modified: 2020/05/03 06:41 by berezovs