You are on page 1of 9

Selected Programming Challenges

Advanced Exercises for Haskell and Other Programming Languages


1 Introduction
These exercises are optional and unassessed and you should only spend time on them if you feel
you need a new challenge that takes you beyond the unassessed problems and weekly lab exercises.
The exercises are taken from the text book:
Steven S. Skiena and Miguel A. Rivella, Programming Challenges the Pro-
gramming Contest Training Manual, Springer 2003
and reproduced with permission from the authors. The text of each problem has been copied
largely without modication, although the input and output specications have been modied so
that you dont have to do any explicit Haskell I/O. The exercises, without any supporting text or
hints, can also be dowloaded from
http://www.programming-challenges.com
The purpose of setting you these exercises is twofold. Firstly, they provide an opportunity
for you to practice your Haskell programming skills; in particular, you should nd that many of
the problems have extremely succinct and elegant solutions when you make full use of Haskells
higher-order functions and list comprehensions. Most importantly, however, they exercise your
problem-solving skills.
Inventing a smart way to solve a problem is an valuable skill in its own right. Some people are
naturally better at problem solving than others, but everyone can hone their skills with practice.
These exercises give you a chance to do that. If you get really good at it, you might consider
entering one of the national or international programming competitions, for example:
http://icpc.baylor.edu/icpc/
http://www.bcs.org/BCS/Awards/Events/ProgrammingCompetition/
You dont have to solve the problems in Haskell, of course, but are encouraged to do so, even if
you also produce solutions in other languages. In many cases a good Haskell solution is a fraction
of the size of a good solution in a language like C++ or Java. If youre not convinced, try it,
although youll need to modify the specication of the programs inputs and outputs, or learn how
to do I/O in Haskell.
If you enjoy these exercises, you are encouraged to look at the other problems in the book over
the coming year, and beyond. You might even like to invent problems of your own. If you think
you have invented an interesting and challenging new problem, please mail it to me (but dont
show me the solution!).
There are thirteen problems in total. I have chosen ve problems which I consider to be
relatively easy, ve that Ive classed as being of intermediate diculty and two that have a bit of
a sting in their tail! At the end Ive added an additional problem thats not in the book, inspired
by a popular day-time TV show.
1
2 Easy problems
These problems can be solved in just a few lines of Haskell code (of the order of 110 lines of
neatly formatted code).
2.1 Ones
Given any integer 0 n 10, 000 not divisible by 2 or 5, some multiple of n is a number which
in decimal notation is a sequence of 1s. How many digits are in the smallest such multiple of n?
Dene a function digits :: Integer -> Integer that, given an integer a, returns the small-
est integer x > 0 such that p =

x1
i=0
1 10
i
, p = a b, and b is an integer greater than zero. For
example,
Main> digits 9901
12
Main> digits 3
3
Main> digits 7
6
Main> digits 9901
12
Note: Using Integer types is cheating somewhat, but it makes the (smart) solution extremely
simple!
2.2 The 3n + 1 Problem
Consider the following algorithm to generate a sequence of numbers. Start with an integer n. If n
is even, divide by 2. If n is odd, multiply by 3 and add 1. Repeat this process, terminating when
you reach 1. For example, the following sequence of numbers will be generated for n = 22: 22 11
34 17 52 26 13 40 20 10 5 16 8 4 2 1 It is conjectured (but not yet proven) that this algorithm
will terminate at 1 for every starting integer n. Still, the conjecture holds for all integers up to at
least 1,000,000.
For an input n, the cycle-length of n is the number of numbers generated up to and including
the 1. In the example above, the cycle length of 22 is 16. Given any two numbers i and j, you
are to determine the maximum cycle length over all numbers between i and j, including both
endpoints.
Dene a function solve :: Int -> Int -> ( Int, Int, Int ) that given two integers i
and j, will return i, j in the same order in which they appeared in the input, and the maximum
cycle length for integers between and including i and j. For example,
Main> solve 1 10
(1,10,20)
Main> solve 100 200
(100,200,125)
Main> solve 201 210
(201,210,89)
Main> solve 900 1000
(900,1000,174)
2.3 Jolly Jumpers
A sequence of n > 0 integers is called a jolly jumper if the absolute values of the dierences
between successive elements take on all possible values 1 through n 1. For instance,
1 4 2 3
2
is a jolly jumper, because the absolute dierences are 3, 2, and 1, respectively. The denition
implies that any sequence of a single integer is a jolly jumper. Write a program to determine
whether each of a number of sequences is a jolly jumper.
Dene a function isJumper :: [ Int ] -> Bool that, given a list of integers returns True
i the numbers constitute a jolly jumper. For example,
Main> isJumper [1,4,2,3]
True
Main> isJumper [1,4,2,-1,6]
False
2.4 Hartals
Political parties in Bangladesh show their muscle by calling for regular hartals (strikes), which
cause considerable economic damage. For our purposes, each party may be characterized by a
positive integer h called the hartal parameter that denotes the average number of days between
two successive strikes called by the given party.
Consider three political parties. Assume h
1
= 3, h
2
= 4, and h
3
= 8, where h
i
is the hartal
parameter for party i. We can simulate the behavior of these three parties for N = 14 days. We
always start the simulation on a Sunday. There are no hartals on either Fridays or Saturdays.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
Days Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa
--------------------------------------------------------------
Party 1 x x x x
Party 2 x x x
Party 3 x
--------------------------------------------------------------
Hartals 1 2 3 4 5
There will be exactly ve hartals (on days 3, 4, 8, 9, and 12) over the 14 days. There is no hartal
on day 6 since it falls on Friday. Hence we lose ve working days in two weeks.
Dene a function hartals :: Int -> [ Int ] -> Int that, given the value of N and the
hartal parameters for several political parties, returns the number of working days lost in those N
days. For example,
Main> hartals 14 [3,4,8]
5
Main> hartals 100 [12,15,25,40]
15
2.5 The Trip
A group of students are members of a club that travels annually to dierent locations. Their
destinations in the past have included Indianapolis, Phoenix, Nashville, Philadelphia, San Jose,
and Atlanta. This spring they are planning a trip to Eindhoven.
The group agrees in advance to share expenses equally, but it is not practical to share every
expense as it occurs. Thus individuals in the group pay for particular things, such as meals,
hotels, taxi rides, and plane tickets. After the trip, each students expenses are tallied and money
is exchanged so that the net cost to each is the same, to within one cent. In the past, this money
exchange has been tedious and time consuming. Your job is to compute, from a list of expenses,
the minimum amount of money that must change hands in order to equalize (within one cent) all
the students costs.
Dene a function trip :: [ Float ] -> Float that, given a list of the amount spent by
each student, returns the total amount of money, in dollars and cents, that must be exchanged to
3
equalize the students costs. There are no more than 1000 students and no student spent more
than $10,000.00. For example,
Main> trip [10,20,30]
10.0
Main> trip [15,15.01,3,3.01]
11.99
3 Intermediate Problems
These require a little more thinking and, in some cases, a little more code.
3.1 A Multiplication Game
Stan and Ollie play the game of multiplication by multiplying an integer p by one of the numbers
2 to 9. Stan always starts with p = 1, does his multiplication, then Ollie multiplies the number,
then Stan, and so on. Before a game starts, they draw an integer 1 < n < 4294967295 and the
winner is whoever reaches p n rst.
Dene a function play :: Int -> String that, given n will return either the string Stan
wins or Ollie wins assuming that both of them play perfectly. For example,
Main> play 162
"Stan wins"
Main> play 17
"Ollie wins"
Main> play 34012226
"Stan wins"
3.2 Stern-Brocot Number System
The Stern-Brocot tree is a beautiful way for constructing the set of all i non-negative fractions
m
n
where m and n are relatively prime. The idea is to start with two fractions

0
1
,
1
0

and then repeat


the following operation as many times as desired: Insert
m+m

n+n

between two adjacent fractions


m
n
and
m

. For example, the rst step gives us one new entry between
0
1
and
1
0
,
0
1
,
1
1
,
1
0
and the next gives two more:
0
1
,
1
2
,
1
1
,
2
1
,
1
0
The next gives four more:
0
1
,
1
3
,
1
2
,
2
3
,
1
1
,
3
2
,
2
1
,
3
1
,
1
0
The entire array can be regarded as an innite binary tree structure whose top levels are shown
in Figure 3.2.
This construction preserves order, and thus we cannot possibly get the same fraction in two
dierent places. We can, in fact, regard the Stern-Brocot tree as a number system for representing
rational numbers, because each positive, reduced fraction occurs exactly once. Let us use the
letters L and R to stand for going down the left or right branch as we proceed from the root
of the tree to a particular fraction; then a string of Ls and Rs uniquely identies a place in the
tree. For example, LRRL means that we go left from
1
1
down to
1
2
, then right to
2
3
, then right
to
3
4
, then left to
5
7
. We can consider LRRL to be a representation of
5
7
. Every positive fraction
gets represented in this way as a unique string of Ls and Rs. Well, almost every fraction. The
4
1
2
1
3
2
3
1
4
2
5
3
5
3
4
4
3
5
3
5
2
4
1
3
2
3
1
2
1
1
1
1
0
0
1
Figure 1: The rst few levels of the Stern-Brocot tree
fraction
1
1
corresponds to the empty string. We will denote it by I, since that looks something like
1 and stands for identity. In this problem, given a positive rational fraction, represent it in the
Stern- Brocot number system.
Dene a function rep :: ( Int, Int ) -> String that, given a pair of integers m and n
that are relatively prime returns the representation of the given fraction
m
n
in the Stern-Brocot
number system. For example,
Main> rep (5,7)
"LRRL"
Main> rep (878,323)
"RRLRRLRLLLLRLRRR"
3.3 The Priest Mathematician
The ancient folklore behind the Towers of Hanoi puzzle is quite well known. A more recent legend
tells us that once the Brahmin monks discovered how long it would take to nish transferring the
64 discs from the needle which they were on to one of the other needles, they decided to nd a
faster strategy and be done with it.
One of the priests at the temple informed his colleagues that they could achieve the transfer
in single afternoon at a one disc-per-second rhythm by using an additional needle. He proposed
the following strategy:
First move the topmost discs (say the top k discs) to one of the spare needles.
Then use the standard three needles strategy to move the remaining nk discs (for a general
case with n discs) to their destination.
Finally, move the top k discs into their nal destination using the four needles.
He calculated the value of k which minimized the number of movements and found that 18,433
transfers would suce. Thus they could spend just 5 hours, 7 minutes, and 13 seconds with this
scheme versus over 500,000 million years without the additional needle!
Try to follow the clever priests strategy and calculate the number of transfers using four
needles, where the priest can move only one disc at a time and must place each disc on a needle
such that there is no smaller disc below it. Calculate the k that minimizes the number of transfers
under this strategy.
Dene two functions. Firstly a function nMoves :: Int -> Integer that, given the number
of disks, 0 n 100, to be transferred, returns the number of movements required to transfer the
5
n disks from one needle to another using the strategy described. Secondly, dene a function moves
:: Int -> [ ( Int, Int ) ] that, given the number of disks, 0 n 100, to be transferred,
returns the sequence of movements required to move the n disks from one needle to another.
Assume the needles are numbered 1, 2, 3 and 4 and that the disks are to be moved from needle
1 to needle 4 using needles 2 and 3 as spares. Note: the use of Integer arithmetic will avoid
problems of arithmetic overow. For example,
Main> nMoves 1
1
Main> nMoves 28
769
Main> nMoves 64
18433
Main> moves 3
[(1,3),(1,2),(1,4),(2,4),(3,4)]
Main> moves 8
[(1,2),(1,3),(1,4),(3,4),(1,3),(4,1),(4,3),(1,3),(2,3),(1,2),
(1,4),(2,4),(1,2),(4,1),(4,2),(1,2),(1,4),(2,4),(2,1),(4,1),
(2,4),(1,2),(1,4),(2,4),(3,2),(3,4),(3,1),(4,1),(3,4),(1,3),
(1,4),(3,4),(2,4)]
Hint: The key to an ecient solution here is a technique called memoisation which turns out to
be very closely related to the general problem-solving technique of dynamic programming. If you
write a recursive function to solve the problem of nding the number of movements and optimum
k for a pile of n disks youll nd that you end up recomputing that function repeatedly with the
same value of n, c.f. computing Fibonnaci numbers the naive way. To get round this try building
a table of pairs of the form (numbers of moves, optimum k) for each value of n from 0, 1,
upwards. The purpose of the table is to remember values previously computed by the function,
i.e. it is a look-up table mapping a given n to a corresponding pair. In Haskell, the table might
be implemented as a list where each element is dened in terms of previously computed elements.
A really neat way to do this is to build an innite list where the n
th
element delivers the numbers
of moves and optimum k for n disks, n = 0, 1, 2....
3.4 Crypt Kicker
A common but insecure method of encrypting text is to permute the letters of the alphabet. In
other words, each letter of the alphabet is consistently replaced in the text by some other letter.
To ensure that the encryption is reversible, no two letters are replaced by the same letter.
Your task is to decrypt several encoded lines of text, assuming that each line uses a dierent set
of replacements, and that all words in the decrypted text are from a dictionary of known words.
Dene a function decrypt :: [ String ] -> String -> String that, given a dictionary
of words and an encrypted string, returns the string decrypted using the dictionary. If there are
multiple solutions, any one will do. If there is no solution, replace every letter of the alphabet by
an asterisk. For example,
Main> decrypt ["and","dick","jane","puff","spot","yertle"]
"bjvg xsb hxsn xsb qymm xsb rqat xsb pnetfn"
"dick and jane and puff and spot and yertle"
Main> decrypt ["and","dick","jane","puff","spot","yertle"]
"xxxx yyy zzzz www yyyy aaa bbbb ccc dddddd"
"**** *** **** *** **** *** **** *** ******"
6
3.5 Poker Hands
A poker deck contains 52 cards. Each card has a suit of either clubs, diamonds, hearts, or spades.
Each card also has a value of either 2 through 10, jack, queen, king, or ace. For scoring purposes
card values are ordered as above, with 2 having the lowest and ace the highest value. The suit
has no impact on value.
A poker hand consists of ve cards dealt from the deck. Poker hands are ranked by the following
partial order from lowest to highest.
1. High Card Hands which do not t any higher category are ranked by the value of their
highest card. If the highest cards have the same value, the hands are ranked by the next
highest, and so on.
2. Pair Two of the ve cards in the hand have the same value. Hands which both contain a
pair are ranked by the value of the cards forming the pair. If these values are the same, the
hands are ranked by the values of the cards not forming the pair, in decreasing order.
3. Two Pairs The hand contains two dierent pairs. Hands which both contain two pairs are
ranked by the value of their highest pair. Hands with the same highest pair are ranked by
the value of their other pair. If these values are the same the hands are ranked by the value
of the remaining card.
4. Three of a Kind Three of the cards in the hand have the same value. Hands which both
contain three of a kind are ranked by the value of the three cards.
5. Straight The hand contains ve cards with consecutive values. Hands which both contain
a straight are ranked by their highest card.
6. Flush The hand contains ve cards of the same suit. Hands which are both ushes are
ranked using the rules for High Card.
7. Full House Three cards of the same value, with the remaining two cards forming a pair.
Ranked by the value of the three cards.
8. Four of a Kind Four cards with the same value. Ranked by the value of the four cards.
9. Straight Flush Five cards of the same suit with consecutive values. Ranked by the highest
card in the hand.
Your job is to compare several pairs of poker hands and to indicate which, if either, has a
higher rank.
A pair of hands is a designation of ten cards: the rst ve cards are the hand for the player
named Black and the next ve cards are the hand for the player named White. Each card
has a value (2,3,...,14) where 14 denotes an ace, and a suit, which is a number from 0 to 3 (clubs,
diamonds, hearts and spades respectively). Thus:
type Hands = [ (Int, Int) ]
Dene a function score :: Hand -> String that, given the ten cards dening the two hands,
returns a string which is either "Black wins", "White wins" or "Tie" depending on the relative
value of the two hands. For example,
Main> score [(2,2),(3,1),(5,3),(9,0),(13,1),(2,0),(3,2),(4,3),(8,0),(14,2)]
"White wins"
Main> score [(2,2),(4,3),(4,1),(2,1),(4,2),(2,3),(8,3),(14,3),(12,3),(3,3)]
"Black wins"
Main> score [(2,2),(3,1),(5,3),(9,0),(13,1),(2,0),(3,2),(4,3),(8,0),(13,2)]
"Black wins"
Main> score [(2,2),(3,1),(5,3),(9,0),(13,1),(2,1),(3,2),(5,0),(9,3),(13,2)]
"Tie"
7
Note that these correspond to the four deals:
2H 3D 5S 9C KD 2C 3H 4S 8C AH
2H 4S 4C 2D 4H 2S 8S AS QS 3S
2H 3D 5S 9C KD 2C 3H 4S 8C KH
2H 3D 5S 9C KD 2D 3H 5C 9S KH
4 Hard Problems
These are particularly ddly problems requiring rather more thought. You may nd that you
think youve solved the problem, only to discover that a deep subtlety lies within. Your testing
strategy is crucial to success.
4.1 Bridge
A group of n people wish to cross a bridge at night. At most two people may cross at any time,
and each group must have a ashlight. Only one ashlight is available among the n people, so
some sort of shuttle arrangement must be arranged in order to return the ashlight so that more
people may cross.
Each person has a dierent crossing speed; the speed of a group is determined by the speed of
the slower member. Your job is to determine a strategy that gets all n people across the bridge
in the minimum time.
Dene a function solve :: [ Int ] -> ( Int, [ [ Int ] ] ) that, given the crossing
times for each of the people, returns the total number of seconds required for all n people to cross
the bridge, and a strategy for achieving this time. Each entry in the strategy is a list containing
either one or two integers, indicating which person or people form the next group to cross. Each
person is indicated by the crossing time specied in the input. Although many people may have
the same crossing time, this ambiguity is of no consequence.
Note that the crossings alternate directions, as it is necessary to return the ashlight so that
more may cross. If more than one strategy yields the minimal time, any one will do. For example
Main> solve [1,2,5,10]
(17,[[1,2],[1],[10,5],[2],[1,2]])
4.2 Pairsumonious Numbers
Any set of n integers form n(n1)/2 sums by adding every possible pair. Your task is to nd the
n integers given the set of sums.
Dene a function solve :: [ Int ] -> [ [ Int ] ] that, given n(n1)/2 integers, returns
n integers in non-descending order such that the input numbers are pairwise sums of the n numbers.
If there is more than one solution, any one will do. If there is no solution, return the empty list.
For example,
Main> solve [1269,1160,1663]
[383,777,886]
Main> solve [1,1,1]
[]
Main> solve [226,223,225,224,227,229,228,226,225,227]
[111,112,113,114,115]
Main> solve[ 216,210,204,212,220,214,222,208,216,210]
[101,103,107,109,113]
Main> solve [-1,0,-1,-2,1,0,-1,1,0,-1]
[-1,-1,0,0,1]
8
5 Bonus Problem Countdown
Countdown is a popular day-time TV quiz show, one component of which is a numbers game.
Contestants select six cards, face down, each of which has a number printed on it. A random
number generator then picks a random target total. The game involves combining the numbers
on the cards using the operators +, -, * and /, to equal the total. Contestants have 30 seconds
to solve the problem. Each number can be used at most once (or not at all) and only integer
arithmetic is permitted. Thus / can only be used provided the rst operand is an integer multiple
of the second. Your job is is to show the contestants how to they could have solved the problem,
should they fail to solve it themselves in the time available. In other words, youre task is to build
a virtual Carol Vorderman
1
!
Dene a function solve :: [ Int ] -> Int -> String that takes the card numbers and
the target total and computes a solution to the game, if one exists. In this variation of the game
there will be at least one card and at most six, and each card, and target, may assume any integer
value. The solution should be a string comprising the text of a valid Haskell expression that, when
computed, will deliver the required target value. If there is more than one solution any one will
do. If there is no solution return "". For example,
Main> [2,4,5,9,75] 658
"(2*(9+(4*(5+75))))"
Main> solve [9,1,4,8] 26
""
Main> solve [9,1,4,8] (-17)
"(1-((9*8)/4))"
1
In case you dont know, Carol Vorderman is the brainy assistant on the show who can invariably nd a solution
when the contestants cant.
9

You might also like