Title: Advice on BIO Problems
1Advice on BIO Problems
- Especially questions 2 and 3
By John Mellor
2The British Informatics Olympiad
- The British Informatics Olympiad (BIO) is an
annual computer programming competition for
pre-university students. The first round is a 3
hour exam, then the top 15 competitors attend a
final in Cambridge over the Easter holidays, of
which the best 4 are chosen to represent Britain
internationally.
3A Word About Question 1s
- Question 1s vary a lot, but it is generally
fairly simple to see a solution. There are some
basics you need to know though - Being able to parse input, whatever its form
(integer, string, or possibly decimal) - Being able to convert between datatypes (string
to integer, integer to string, etc.) - String manipulation (character at index,
searching, substrings, etc.) - Being able to output anything
4Question 2s
- These vary enormously, and though they are
always about implementing something, the details
are unpredictable. If you just plan carefully
what you have to do though, the task is generally
fairly straightforward. - Make a list of all the data you are given, must
store, and must output. - Then list all the operations you must perform on
the data, and use these to form a plan of what
functions you need to write, with some code to
control the functions. - Now break the operations into pieces andwrite
them!
5Important Things For Question 2s
- Ideally you should know how to create use
- Multi-dimensional arrays of any length (including
length told to you at runtime) and any type (e.g.
objects) - Arrays of dynamic length (aka ArrayLists or
Vectors) which grow automatically as you add more
to them) - Objects (mainly so you can group several bits of
data together) - Hashtables (aka Maps) could be useful
- Sorting arrays etc. could come inhandy
6Question 3s
- These are often shorter to write than solutions
to question 2, but require more thought. - The most important thing is to spend a while
planning exactly what youre going to do before
you start writing it. - They are arguably more predictable however, and a
few techniques can get you a long way.
7Recursive Algorithms
- You can use these when a problem has a basic case
which is trivial to solve (or simply known). - Recursive algorithms solve a problem by
repeatedly reducing it to a problem one step
closer to the basic case
if (trivial case) solve directly else solve in
terms of a slightly easier case
8Recursion Example Factorials
- Lets look at how you could find n!. It is defined
as - n! 1 x 2 x ... x (n-2) x (n-1) x n
- (Note that you could solve this particular
problem just by using a loop, but in more complex
cases loops might be unmanageable)
function factorial(int n) if (n 1 n
0) return 1 return n factorial(n-1)
9Dynamic Programming(aka Doing things the easy
way)
- When using recursion you do need to watch out
that you dont repeatedly solve the same problem.
For example, the following recursive algorithm
will find the nth Fibonacci number - But it is very slow for large values of n
function fib(int n) if (n1 n2) return
1 return fib(n-1) fib (n-2)
10Fibonacci example part 2
- because it works all the intermediate steps
between fib(n) and fib(1) many times over
and fib(2) is defined in terms of fib(1) and
fib(0)
11Start small
- The best way to solve this would be to start by
working out smaller Fibonacci numbers and working
upwards
function fib(int n) int terms new
intn1 terms0 1 terms1 1 for (int
i2 iltn i) termsi termsi-2termsi-1
return termsn
This way each number would only be found once
12An easier way to start small
- However as I said before, it is often easier to
just write the first recursive function you can
think of than to work out what the most efficient
way of tackling a problem would be. - So the trick is write your recursive function,
then cheat to make it faster! - Simply store everything you work out in an array,
and whenever you would work something out, first
check if its stored and if so use the stored
version instead.
13Recursive Fibonacci with Cheat
- Heres an example I took the existing
recursive Fibonacci function, and added the red
code to it
int done //I assume this has been initialised
and defaults to 0 function fib(int n) if
(donen ! 0) return donen if (n0 n1)
return 1 donen fib(n-1) fib
(n-2) return donen
14Useful data-structures to cheat with
- If the argument(s) to your function is/are
integers, then you can use these as indexes in an
array (as I did for Fibonacci) - For anything else, you might well want to use a
Hashtable (aka Map). These let you use any object
as the identifier you use to retrieve results
youve found previously (though its often
simplest to turn things into a string as you can
easily concatenate several values into one string)
15Fibonacci Cheat with a Hashtable
- Just as an example
- (note that String.valueOf(n) is just an example
(in Java) of how to turn n an integer into an
object, and isnt even a particularly good way.
Replace it with a customised conversion)
Hashtable done new Hashtable() function
fib(int n) if (done.containsKey(
String.valueOf(n) )) return done.get(String.valueO
f(n)) if (n0 n1) return 1 done.put(
String.valueOf(n), fib(n-1) fib (n-2)
) return done.get( String.valueOf(n) )
16Another Dynamic Programming Example
- Suppose you need to calculate the highest
possible sum of numbers passed on a route from
the top to the bottom of this triangle, with
every step diagonally down - 7
- 3 8
- 8 1 0
- 2 7 4 4
- 4 5 2 6 5
17Another Dynamic Programming Example (part 2)
- The obvious, but slow, way is to write a
recursive function which gives the answer in
terms of the highest sum path of the two sub
triangles starting diagonally down to the left
and right of it.
function highestSum(int row, int col) if (row
num_rows-1) return trianglerowcol int
left highestSum(row1, col) int right
highestSum(row1, col1) return
trianglerowcol max(left, right)
18Another Dynamic Programming Example (part 3)
- However this computes each sub-triangle many
times. - The clever, and quick, way is to start from the
bottom - 30
- 7
- 23
- 23 21
- 3 8
- 20 13
- 20 13 10
- 8 1 0
- 12 12 10
- 7 12 10 10
- 2 7 4 4
- 5 5 6 6
- 4 5 2 6 5
19Another Dynamic Programming Example (part 4)
- The code would look something like this
function highestSum() for (int rowrows-2
rowgt0 row--) for (int col0 colltrow-1
col) trianglerowcol max(
trianglerow1col, trianglerow1col1
) return triangle00
20Another Dynamic Programming Example (part 5)
- But again we could just use the obvious approach
and cheat to make it as fast
int done //I assume this has been
initialised and all set to -1 function
highestSum(int row, int col) if
(donerowcol ! -1) return donerowcol if
(row num_rows-1) return trianglerowcol in
t left highestSum(row1, col) int right
highestSum(row1, col1) donerowcol
trianglerowcol max(left, right) return
donerowcol
Below is an example of how you would actually use
this function
done new intnum_rowsnum_rows //since same
no. of rows as cols for (int x0 xltnum_rows
x) for (int y0 yltnum_rows y) donexy
-1 int ans highestSum(0, 0)
21Backtracking
- Backtracking is a general problem solving tool
for when you must find a state which satisfies
several criteria. - Backtracking effectively tries every possibility
until it finds one that works. - It can be thought of as solving a maze, where you
have a starting point and try to reach an end
point. Whenever backtracking has a choice of path
it will choose one at random and follow that path
and all subpaths until it has exhausted all
possibilities resulting from that pathchoice, or
finds a solution.
22But its not all about mazes!
- All kinds of computing problems can be solved by
backtracking. - Note that although trying every possibility
sounds very slow, backtracking algorithms will
eliminate partial solutions, hence eliminating
the entire set of solutions that the partial
solution represents without trying every one
individually.
23So when do you do it?
- When the solution to the problem
- Can be constructed in a series of steps, with
each step adding a part to the solution. - At every step there will be several ways to
extend the partial solution from the previous
step, some of which will lead to a solution,
while others wont.
24And how do you do it?
- This is where recursive algorithms really come in
handy.
function solve() if (done) return true for
(each new step possible from current position)
make step, storing it in some global data
structure if ( solve() ) return true undo
step, removing it from the data
structure return false //n.b. you could
just pass variables as parameters to the function
instead of storing them in a global data structure
25A slight variation on backtracking
- Sometimes you are required to output the number
of solutions instead. Heres how you could do so
int solutions 0 function solve() if (done)
solutions solutions 1 else for (each new
step possible from current position) make
step, storing it in some global data
structure solve() undo step, removing it
from the data structure)
26For example BIO 2005 q3 - Movies
- A group of actors
- must each film a
- certain number of
- scenes.
- Only one actor is in each scene.
- Each actor must shoot his/her scenes in order.
- Actors vary in seniority and can never have
filmed more scenes than their seniors. - Find the number of
- different orders in
- which the scenes
- could be shot.
int actors int scenes int done function
main() actors inputInteger() scenes new
intactors done new intactors for (int
i0 iltactors i) scenesi
inputInteger() int solutions
solve() print(solutions) function solve()
int solutions 0 for (int i0 iltactors
i) if (scenesi gt 0 (i0 donei lt
donei-1)) scenesi-- donei sol
utions solve() scenesi donei--
for (int i0 iltactors i) if (scenesi
gt 0) return solutions return 1
27And yes, you can cheat here too
int actors int scenes int done Hashtable
done new Hashtable() //note that an eight
dimensional array could have function main()
//been used instead of a Hashtable actors
inputInteger() scenes new intactors done
new intactors for (int i0 iltactors i)
scenesi inputInteger() int solutions
solve() print(solutions) function solve()
if (done.containsKey(getKey())) return
done.get(getKey()) int solutions 0 for (int
i0 iltactors i) if (scenesi gt 0
(i0 donei lt donei-1)) scenesi--
donei solutions solve() scenesi
donei-- for (int i0 iltactors i)
if (scenesi gt 0) done.put(getKey(),
solutions) return solutions done.put(getKey(
), 1) return 1 function getKey() String key
"" for (int i0 iltactors i) key
donei return key
28Sensible sizes
- You can store a maximum of about 8,000,000
integers or 32,000,000 characters. -
- performing a simple operation a billion times
takes about 5 seconds, so if you have, say, -
- that will use up all your time
for (int x0xlt1000x) for (int
y0ylt1000y) for (int z0zlt1000z) do
something
29Getting help
- Make sure you know how to use your programming
languages help systems. - You can use pretty much any relevant help system
except websites, so remember to download the
manual, your favourite tutorial, etc. - As practice, you could try finding out about
things I mentioned above using your choice of
manual/tutorial (e.g. dynamic length arrays,
Hashtables, sorting arrays)
30Help! It doesnt work!
- Once youve finished your program, make sure
that it works for the sample input. If you have
time its also nice to make a few of your own
test cases for a more thorough test. - If it gives you an error message, that will
hopefully be enough to find the mistake. - If it runs fine but gives the wrong answer or
takes more than about 30 seconds, youll have to
find it yourself
31Finding Mistakes
- Re-read the question thoroughly and make sure you
took every piece of information into account. - Check that all your code seems to be doing the
right thing, and that numbers you have written
are not one to high or too low! - Most programming environments include a debugger.
These let you run your code line by line,
checking the values of variables as yougo, and
can be very useful forfinding mistakes.
32About marking
- Many/most of the marks are just for a correct
solution, only a few of the test cases seriously
test your programs speed, so dont worry too
much about efficiency as long as it works. - Dr Forster often tests the simplest cases for a
few marks a pop, as many programs work for all
but the simplest cases! You can of course
hardcode them - If you have time left over at the end you can
probably get about 4 marks per question you
couldnt solve just by hardcoding the example and
the simplest cases - Written qs are often possible even if you didnt
get - the program done