Sie sind auf Seite 1von 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 modification, although the input and output specifications have been modified so
that you don’t 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 find that many of
the problems have extremely succinct and elegant solutions when you make full use of Haskell’s
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 don’t 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 you’re not convinced, try it,
although you’ll need to modify the specification 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 don’t
show me the solution!).
There are thirteen problems in total. I have chosen five problems which I consider to be
relatively easy, five that I’ve classed as being of intermediate difficulty and two that have a bit of
a sting in their tail! At the end I’ve added an additional problem that’s 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 1–10 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 1’s. How many digits are in the smallest such multiple of n?
Define a function digits ::PInteger -> Integer that, given an integer a, returns the small-
x−1
est integer x > 0 such that p = i=0 1 × 10i , 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.
Define 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 differences
between successive elements take on all possible values 1 through n − 1. For instance,
1423

2
is a jolly jumper, because the absolute differences are 3, 2, and 1, respectively. The definition
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.
Define a function isJumper :: [ Int ] -> Bool that, given a list of integers returns True
iff 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 h1 = 3, h2 = 4, and h3 = 8, where hi 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 five 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 five working days in two weeks.
Define 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 different 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 student’s 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.
Define 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 first.
Define 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
0 1

where m and n are relatively prime. The idea is to start with two fractions 1 , 0 and then repeat

the following operation as many times as desired: Insert m+m
n+n′ between two adjacent fractions n
m

and m 0
n′ . For example, the first step gives us one new entry between 1 and 0 ,
1

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 2 1 3 2 3 1
, , , , , , , ,
1 3 2 3 1 2 1 1 0
The entire array can be regarded as an infinite 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
different 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 L’s and R’s uniquely identifies a place in the
tree. For example, LRRL means that we go left from 11 down to 12 , then right to 23 , then right
to 43 , then left to 57 . We can consider LRRL to be a representation of 75 . Every positive fraction
gets represented in this way as a unique string of L’s and R’s. Well, almost every fraction. The

4
0 1
1 0
1
1

1 2
2 1

1 2 3 3
3 3 2 1

1 2 3 3 4 5 5 4
4 5 5 4 3 3 2 1

Figure 1: The first few levels of the Stern-Brocot tree

fraction 11 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.
Define 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 finish transferring the
64 discs from the needle which they were on to one of the other needles, they decided to find 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 n−k discs (for a general
case with n discs) to their destination.
• Finally, move the top k discs into their final destination using the four needles.

He calculated the value of k which minimized the number of movements and found that 18,433
transfers would suffice. 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 priest’s 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.
Define 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, define 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 overflow. 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 efficient 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 finding the number of movements and optimum
k for a pile of n disks you’ll find 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 defined in terms of previously computed elements.
A really neat way to do this is to build an infinite list where the nth 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 different set
of replacements, and that all words in the decrypted text are from a dictionary of known words.
Define 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 five 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 fit 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 five 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 different 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 five cards with consecutive values. Hands which both contain
a straight are ranked by their highest card.
6. Flush The hand contains five cards of the same suit. Hands which are both flushes 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 first five cards are the hand for the player
named “Black” and the next five 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) ]
Define a function score :: Hand -> String that, given the ten cards defining 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 fiddly problems requiring rather more thought. You may find that you
think you’ve 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 flashlight. Only one flashlight is available among the n people, so
some sort of shuttle arrangement must be arranged in order to return the flashlight so that more
people may cross.
Each person has a different 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.
Define 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 specified 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 flashlight 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(n − 1)/2 sums by adding every possible pair. Your task is to find the
n integers given the set of sums.
Define a function solve :: [ Int ] -> [ [ Int ] ] that, given n(n−1)/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 first 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, you’re task is to build
a virtual “Carol Vorderman”1!
Define 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 don’t know, Carol Vorderman is the brainy assistant on the show who can invariably find a solution

when the contestants can’t.

Das könnte Ihnen auch gefallen