100% found this document useful (1 vote)
2K views448 pages

Competitive Programming 3 PDF

Uploaded by

Juliana Cardozo
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
100% found this document useful (1 vote)
2K views448 pages

Competitive Programming 3 PDF

Uploaded by

Juliana Cardozo
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 448

Contents

Foreword vi

Preface viii

Authors’ Profiles xix

List of Abbreviations xx

List of Tables xxi

List of Figures xxii

1 Introduction 1
1.1 Competitive Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Tips to be Competitive . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.1 Tip 1: Type Code Faster! . . . . . . . . . . . . . . . . . . . . . . . . 3
1.2.2 Tip 2: Quickly Identify Problem Types . . . . . . . . . . . . . . . . . 4
1.2.3 Tip 3: Do Algorithm Analysis . . . . . . . . . . . . . . . . . . . . . . 6
1.2.4 Tip 4: Master Programming Languages . . . . . . . . . . . . . . . . . 10
1.2.5 Tip 5: Master the Art of Testing Code . . . . . . . . . . . . . . . . . 13
1.2.6 Tip 6: Practice and More Practice . . . . . . . . . . . . . . . . . . . 15
1.2.7 Tip 7: Team Work (for ICPC) . . . . . . . . . . . . . . . . . . . . . . 16
1.3 Getting Started: The Easy Problems . . . . . . . . . . . . . . . . . . . . . . 16
1.3.1 Anatomy of a Programming Contest Problem . . . . . . . . . . . . . 16
1.3.2 Typical Input/Output Routines . . . . . . . . . . . . . . . . . . . . . 17
1.3.3 Time to Start the Journey . . . . . . . . . . . . . . . . . . . . . . . . 19
1.4 The Ad Hoc Problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
1.5 Solutions to Non-Starred Exercises . . . . . . . . . . . . . . . . . . . . . . . 27
1.6 Chapter Notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

2 Data Structures and Libraries 33


2.1 Overview and Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
2.2 Linear DS with Built-in Libraries . . . . . . . . . . . . . . . . . . . . . . . . 35
2.3 Non-Linear DS with Built-in Libraries . . . . . . . . . . . . . . . . . . . . . 43
2.4 Data Structures with Our Own Libraries . . . . . . . . . . . . . . . . . . . . 49
2.4.1 Graph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
2.4.2 Union-Find Disjoint Sets . . . . . . . . . . . . . . . . . . . . . . . . . 52
2.4.3 Segment Tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
2.4.4 Binary Indexed (Fenwick) Tree . . . . . . . . . . . . . . . . . . . . . 59
2.5 Solution to Non-Starred Exercises . . . . . . . . . . . . . . . . . . . . . . . . 64
2.6 Chapter Notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67

i
CONTENTS 
c Steven & Felix

3 Problem Solving Paradigms 69


3.1 Overview and Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
3.2 Complete Search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
3.2.1 Iterative Complete Search . . . . . . . . . . . . . . . . . . . . . . . . 71
3.2.2 Recursive Complete Search . . . . . . . . . . . . . . . . . . . . . . . . 74
3.2.3 Tips . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
3.3 Divide and Conquer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
3.3.1 Interesting Usages of Binary Search . . . . . . . . . . . . . . . . . . . 84
3.4 Greedy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
3.4.1 Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
3.5 Dynamic Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
3.5.1 DP Illustration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
3.5.2 Classical Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
3.5.3 Non-Classical Examples . . . . . . . . . . . . . . . . . . . . . . . . . 112
3.6 Solution to Non-Starred Exercises . . . . . . . . . . . . . . . . . . . . . . . . 118
3.7 Chapter Notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120

4 Graph 121
4.1 Overview and Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
4.2 Graph Traversal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
4.2.1 Depth First Search (DFS) . . . . . . . . . . . . . . . . . . . . . . . . 122
4.2.2 Breadth First Search (BFS) . . . . . . . . . . . . . . . . . . . . . . . 123
4.2.3 Finding Connected Components (Undirected Graph) . . . . . . . . . 125
4.2.4 Flood Fill - Labeling/Coloring the Connected Components . . . . . . 125
4.2.5 Topological Sort (Directed Acyclic Graph) . . . . . . . . . . . . . . . 126
4.2.6 Bipartite Graph Check . . . . . . . . . . . . . . . . . . . . . . . . . . 128
4.2.7 Graph Edges Property Check via DFS Spanning Tree . . . . . . . . . 128
4.2.8 Finding Articulation Points and Bridges (Undirected Graph) . . . . . 130
4.2.9 Finding Strongly Connected Components (Directed Graph) . . . . . . 133
4.3 Minimum Spanning Tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
4.3.1 Overview and Motivation . . . . . . . . . . . . . . . . . . . . . . . . 138
4.3.2 Kruskal’s Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
4.3.3 Prim’s Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
4.3.4 Other Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
4.4 Single-Source Shortest Paths . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
4.4.1 Overview and Motivation . . . . . . . . . . . . . . . . . . . . . . . . 146
4.4.2 SSSP on Unweighted Graph . . . . . . . . . . . . . . . . . . . . . . . 146
4.4.3 SSSP on Weighted Graph . . . . . . . . . . . . . . . . . . . . . . . . 148
4.4.4 SSSP on Graph with Negative Weight Cycle . . . . . . . . . . . . . . 151
4.5 All-Pairs Shortest Paths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
4.5.1 Overview and Motivation . . . . . . . . . . . . . . . . . . . . . . . . 155
4.5.2 Explanation of Floyd Warshall’s DP Solution . . . . . . . . . . . . . 156
4.5.3 Other Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
4.6 Network Flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
4.6.1 Overview and Motivation . . . . . . . . . . . . . . . . . . . . . . . . 163
4.6.2 Ford Fulkerson’s Method . . . . . . . . . . . . . . . . . . . . . . . . . 163
4.6.3 Edmonds Karp’s Algorithm . . . . . . . . . . . . . . . . . . . . . . . 164
4.6.4 Flow Graph Modeling - Part 1 . . . . . . . . . . . . . . . . . . . . . . 166
4.6.5 Other Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
4.6.6 Flow Graph Modeling - Part 2 . . . . . . . . . . . . . . . . . . . . . . 168

ii
CONTENTS 
c Steven & Felix

4.7 Special Graphs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171


4.7.1 Directed Acyclic Graph . . . . . . . . . . . . . . . . . . . . . . . . . . 171
4.7.2 Tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178
4.7.3 Eulerian Graph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
4.7.4 Bipartite Graph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
4.8 Solution to Non-Starred Exercises . . . . . . . . . . . . . . . . . . . . . . . . 187
4.9 Chapter Notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190

5 Mathematics 191
5.1 Overview and Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
5.2 Ad Hoc Mathematics Problems . . . . . . . . . . . . . . . . . . . . . . . . . 192
5.3 Java BigInteger Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
5.3.1 Basic Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
5.3.2 Bonus Features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
5.4 Combinatorics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
5.4.1 Fibonacci Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
5.4.2 Binomial Coefficients . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
5.4.3 Catalan Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
5.4.4 Remarks about Combinatorics in Programming Contests . . . . . . . 206
5.5 Number Theory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
5.5.1 Prime Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
5.5.2 Greatest Common Divisor & Least Common Multiple . . . . . . . . . 211
5.5.3 Factorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
5.5.4 Finding Prime Factors with Optimized Trial Divisions . . . . . . . . . 212
5.5.5 Working with Prime Factors . . . . . . . . . . . . . . . . . . . . . . . 213
5.5.6 Functions Involving Prime Factors . . . . . . . . . . . . . . . . . . . 214
5.5.7 Modified Sieve . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
5.5.8 Modulo Arithmetic . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
5.5.9 Extended Euclid: Solving Linear Diophantine Equation . . . . . . . . 217
5.5.10 Remarks about Number Theory in Programming Contests . . . . . . 217
5.6 Probability Theory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
5.7 Cycle-Finding . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223
5.7.1 Solution(s) using Efficient Data Structure . . . . . . . . . . . . . . . 223
5.7.2 Floyd’s Cycle-Finding Algorithm . . . . . . . . . . . . . . . . . . . . 223
5.8 Game Theory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
5.8.1 Decision Tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
5.8.2 Mathematical Insights to Speed-up the Solution . . . . . . . . . . . . 227
5.8.3 Nim Game . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
5.9 Solution to Non-Starred Exercises . . . . . . . . . . . . . . . . . . . . . . . . 229
5.10 Chapter Notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231

6 String Processing 233


6.1 Overview and Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
6.2 Basic String Processing Skills . . . . . . . . . . . . . . . . . . . . . . . . . . 234
6.3 Ad Hoc String Processing Problems . . . . . . . . . . . . . . . . . . . . . . . 236
6.4 String Matching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
6.4.1 Library Solutions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 241
6.4.2 Knuth-Morris-Pratt’s (KMP) Algorithm . . . . . . . . . . . . . . . . 241
6.4.3 String Matching in a 2D Grid . . . . . . . . . . . . . . . . . . . . . . 244
6.5 String Processing with Dynamic Programming . . . . . . . . . . . . . . . . . 245

iii
CONTENTS 
c Steven & Felix

6.5.1 String Alignment (Edit Distance) . . . . . . . . . . . . . . . . . . . . 245


6.5.2 Longest Common Subsequence . . . . . . . . . . . . . . . . . . . . . . 247
6.5.3 Non Classical String Processing with DP . . . . . . . . . . . . . . . . 247
6.6 Suffix Trie/Tree/Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
6.6.1 Suffix Trie and Applications . . . . . . . . . . . . . . . . . . . . . . . 249
6.6.2 Suffix Tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
6.6.3 Applications of Suffix Tree . . . . . . . . . . . . . . . . . . . . . . . . 251
6.6.4 Suffix Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 253
6.6.5 Applications of Suffix Array . . . . . . . . . . . . . . . . . . . . . . . 258
6.7 Solution to Non-Starred Exercises . . . . . . . . . . . . . . . . . . . . . . . . 264
6.8 Chapter Notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267

7 (Computational) Geometry 269


7.1 Overview and Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
7.2 Basic Geometry Objects with Libraries . . . . . . . . . . . . . . . . . . . . . 271
7.2.1 0D Objects: Points . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
7.2.2 1D Objects: Lines . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
7.2.3 2D Objects: Circles . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
7.2.4 2D Objects: Triangles . . . . . . . . . . . . . . . . . . . . . . . . . . 278
7.2.5 2D Objects: Quadrilaterals . . . . . . . . . . . . . . . . . . . . . . . . 281
7.3 Algorithm on Polygon with Libraries . . . . . . . . . . . . . . . . . . . . . . 285
7.3.1 Polygon Representation . . . . . . . . . . . . . . . . . . . . . . . . . 285
7.3.2 Perimeter of a Polygon . . . . . . . . . . . . . . . . . . . . . . . . . . 285
7.3.3 Area of a Polygon . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285
7.3.4 Checking if a Polygon is Convex . . . . . . . . . . . . . . . . . . . . . 286
7.3.5 Checking if a Point is Inside a Polygon . . . . . . . . . . . . . . . . . 287
7.3.6 Cutting Polygon with a Straight Line . . . . . . . . . . . . . . . . . . 288
7.3.7 Finding the Convex Hull of a Set of Points . . . . . . . . . . . . . . . 289
7.4 Solution to Non-Starred Exercises . . . . . . . . . . . . . . . . . . . . . . . . 294
7.5 Chapter Notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297

8 More Advanced Topics 299


8.1 Overview and Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299
8.2 More Advanced Search Techniques . . . . . . . . . . . . . . . . . . . . . . . 299
8.2.1 Backtracking with Bitmask . . . . . . . . . . . . . . . . . . . . . . . 299
8.2.2 Backtracking with Heavy Pruning . . . . . . . . . . . . . . . . . . . . 304
8.2.3 State-Space Search with BFS or Dijkstra’s . . . . . . . . . . . . . . . 305
8.2.4 Meet in the Middle (Bidirectional Search) . . . . . . . . . . . . . . . 306
8.2.5 Informed Search: A* and IDA* . . . . . . . . . . . . . . . . . . . . . 308
8.3 More Advanced DP Techniques . . . . . . . . . . . . . . . . . . . . . . . . . 312
8.3.1 DP with Bitmask . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312
8.3.2 Compilation of Common (DP) Parameters . . . . . . . . . . . . . . . 313
8.3.3 Handling Negative Parameter Values with Offset Technique . . . . . . 313
8.3.4 MLE? Consider Using Balanced BST as Memo Table . . . . . . . . . 315
8.3.5 MLE/TLE? Use Better State Representation . . . . . . . . . . . . . . 315
8.3.6 MLE/TLE? Drop One Parameter, Recover It from Others . . . . . . 316
8.4 Problem Decomposition . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
8.4.1 Two Components: Binary Search the Answer and Other . . . . . . . 320
8.4.2 Two Components: Involving 1D Static RSQ/RMQ . . . . . . . . . . 322
8.4.3 Two Components: Graph Preprocessing and DP . . . . . . . . . . . . 322

iv
CONTENTS 
c Steven & Felix

8.4.4 Two Components: Involving Graph . . . . . . . . . . . . . . . . . . . 324


8.4.5 Two Components: Involving Mathematics . . . . . . . . . . . . . . . 324
8.4.6 Two Components: Complete Search and Geometry . . . . . . . . . . 324
8.4.7 Two Components: Involving Efficient Data Structure . . . . . . . . . 324
8.4.8 Three Components . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325
8.5 Solution to Non-Starred Exercises . . . . . . . . . . . . . . . . . . . . . . . . 332
8.6 Chapter Notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333

9 Rare Topics 335


9.1 2-SAT Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336
9.2 Art Gallery Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
9.3 Bitonic Traveling Salesman Problem . . . . . . . . . . . . . . . . . . . . . . 339
9.4 Bracket Matching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341
9.5 Chinese Postman Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342
9.6 Closest Pair Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343
9.7 Dinic’s Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
9.8 Formulas or Theorems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
9.9 Gaussian Elimination Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . 346
9.10 Graph Matching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 349
9.11 Great-Circle Distance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 352
9.12 Hopcroft Karp’s Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353
9.13 Independent and Edge-Disjoint Paths . . . . . . . . . . . . . . . . . . . . . . 354
9.14 Inversion Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
9.15 Josephus Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356
9.16 Knight Moves . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357
9.17 Kosaraju’s Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358
9.18 Lowest Common Ancestor . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359
9.19 Magic Square Construction (Odd Size) . . . . . . . . . . . . . . . . . . . . . 361
9.20 Matrix Chain Multiplication . . . . . . . . . . . . . . . . . . . . . . . . . . . 362
9.21 Matrix Power . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
9.22 Max Weighted Independent Set . . . . . . . . . . . . . . . . . . . . . . . . . 368
9.23 Min Cost (Max) Flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369
9.24 Min Path Cover on DAG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370
9.25 Pancake Sorting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371
9.26 Pollard’s rho Integer Factoring Algorithm . . . . . . . . . . . . . . . . . . . . 374
9.27 Postfix Calculator and Conversion . . . . . . . . . . . . . . . . . . . . . . . . 376
9.28 Roman Numerals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
9.29 Selection Problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 380
9.30 Shortest Path Faster Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . 383
9.31 Sliding Window . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 384
9.32 Sorting in Linear Time . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386
9.33 Sparse Table Data Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . 388
9.34 Tower of Hanoi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
9.35 Chapter Notes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391

A uHunt 393

B Credits 396

Bibliography 398

v
CONTENTS 
c Steven & Felix

Foreword
A long time ago (on the 11th of November in 2003, Tuesday, 3:55:57 UTC), I received an
e-mail with the following message:

“I should say in a simple word that with the UVa Site, you have given birth to
a new CIVILIZATION and with the books you write (he meant “Programming
Challenges: The Programming Contest Training Manual” [60], coauthored with
Steven Skiena), you inspire the soldiers to carry on marching. May you live long
to serve the humanity by producing super-human programmers.”

Although that was clearly an exaggeration, it did cause me to think. I had a dream: to
create a community around the project I had started as a part of my teaching job at UVa,
with people from all around the world working together towards the same ideal. With a
little searching, I quickly found a whole online community running a web-ring of sites with
excellent tools that cover and provide whatever the UVa site lacked.
To me, ‘Methods to Solve’ by Steven Halim, a very young student from Indonesia, was
one of the more impressive websites. I was inspired to believe that the dream would become
real one day, because in this website lay the result of the hard work of a genius of algorithms
and informatics. Moreover, his declared objectives matched the core of my dream: to serve
humanity. Even better, he has a brother with similar interests and capabilities, Felix Halim.
It’s a pity that it takes so much time to start a real collaboration, but life is like that.
Fortunately, all of us have continued working together in a parallel fashion towards the
realization of that dream—the book that you have in your hands now is proof of that.
I can’t imagine a better complement for the UVa Online Judge. This book uses lots of
examples from UVa carefully selected and categorized both by problem type and solving
technique, providing incredibly useful help for the users of the site. By mastering and
practicing most programming exercises in this book, a reader can easily solve at least 500
problems in the UVa Online Judge, which will place them in the top 400-500 amongst
≈100000 UVa OJ users.
It’s clear that the book “Competitive Programming: Increasing the Lower Bound of
Programming Contests” is suitable for programmers who want to improve their ranks in
upcoming ICPC regionals and IOIs. The two authors have gone through these contests
(ICPC and IOI) themselves as contestants and now as coaches. But it’s also an essential
colleague for newcomers—as Steven and Felix say in the introduction ‘the book is not meant
to be read once, but several times’.
Moreover, it contains practical C++ source code to implement given algorithms. Un-
derstanding a problem is one thing, but knowing the algorithm to solve it is another, and
implementing the solution well in short and efficient code is tricky. After you have read this
extraordinary book three times you will realize that you are a much better programmer and,
more importantly, a happier person.

vi
CONTENTS 
c Steven & Felix

Miguel A. Revilla, University of Valladolid


UVa Online Judge site creator;
ACM-ICPC International Steering Committee Member and Problem Archivist
http://uva.onlinejudge.org; http://livearchive.onlinejudge.org

vii
CONTENTS 
c Steven & Felix

Preface
This book is a must have for every competitive programmer. Mastering the contents of
this book is a necessary (but maybe not sufficient) condition if one wishes to take a leap
forward from being just another ordinary coder to being among one of the world’s finest
programmers.
Typical readers of this book would include:

1. University students who are competing in the annual ACM International Collegiate
Programming Contest (ICPC) [66] Regional Contests (including the World Finals),
2. Secondary or High School Students who are competing in the annual International
Olympiad in Informatics (IOI) [34] (including the National or Provincial Olympiads),
3. Coaches who are looking for comprehensive training materials for their students [24],
4. Anyone who loves solving problems through computer programs. There are numer-
ous programming contests for those who are no longer eligible for ICPC, including
TopCoder Open, Google CodeJam, Internet Problem Solving Contest (IPSC), etc.

Prerequisites
This book is not written for novice programmers. This book is aimed at readers who have
at least basic knowledge in programming methodology, are familiar with at least one of
these programming languages (C/C++ or Java, preferably both), have passed a basic data
structures and algorithms course (typically taught in year one of Computer Science university
curricula), and understand simple algorithmic analysis (at least the big-O notation). In
the third edition, more content has been added so that this book can also be used as a
supplementary reading for a basic Data Structures and Algorithms course.

To ACM ICPC Contestants

viii
CONTENTS 
c Steven & Felix

We know that one cannot probably win the ACM ICPC regional just by mastering the
contents of the current version (third edition) of this book. While we have included a lot of
materials in this book—much more than in the first two editions—we are aware that much
more than what this book can offer is required to achieve that feat. Some additional pointers
to useful references are listed in the chapter notes for readers who are hungry for more. We
believe, however, that your team will fare much better in future ICPCs after mastering the
contents of this book. We hope that this book will serve as both inspiration and motivation
for your 3-4 year journey competing in ACM ICPCs during your University days.

To IOI Contestants

Much of our advice for ACM ICPC contestants applies to you too. The ACM ICPC and IOI
syllabi are largely similar, except that IOI, for now, currently excludes the topics listed in
the following Table 1. You can skip these items until your university years (when you join
that university’s ACM ICPC teams). However, learning these techniques in advance may
definitely be beneficial as some tasks in IOI can become easier with additional knowledge.
We know that one cannot win a medal in IOI just by mastering the contents of the
current version (third edition) of this book. While we believe that many parts of the IOI
syllabus has been included in this book—hopefully enabling you to achieve a respectable
score in future IOIs—we are well aware that modern IOI tasks require keen problem solving
skills and tremendous creativity—virtues that we cannot possibly impart through this static
textbook. This book can provide knowledge, but the hard work must ultimately be done by
you. With practice comes experience, and with experience comes skill. So, keep practicing!

Topic In This Book


Data Structures: Union-Find Disjoint Sets Section 2.4.2
Graph: Finding SCCs, Network Flow, Bipartite Graphs Section 4.2.1, 4.6.3, 4.7.4
Math: BigInteger, Probability Theory, Nim Games Section 5.3, 5.6, 5.8
String Processing: Suffix Trees/Arrays Section 6.6
More Advanced Topics: A*/IDA* Section 8.2
Many of the Rare Topics Chapter 9

Table 1: Not in IOI Syllabus [20] Yet

ix
CONTENTS 
c Steven & Felix

To Teachers and Coaches


This book is used in Steven’s CS3233 - ‘Competitive Programming’ course in the School
of Computing at the National University of Singapore. CS3233 is conducted in 13 teaching
weeks using the following lesson plan (see Table 2). The PDF slides (only the public version)
are given in the companion web site of this book. Fellow teachers/coaches should feel free to
modify the lesson plan to suit students’ needs. Hints or brief solutions of the non-starred
written exercises in this book are given at the back of each chapter. Some of the starred
written exercises are quite challenging and have neither hints nor solutions. These can
probably be used as exam questions or contest problems (of course, solve them first!).
This book is also used as a supplementary reading in Steven’s CS2010 - ‘Data Struc-
tures and Algorithms’ course, mostly for the implementation of several algorithms and writ-
ten/programming exercises.

Wk Topic In This Book


01 Introduction Ch 1, Sec 2.2, 5.2, 6.2-6.3, 7.2
02 Data Structures & Libraries Chapter 2
03 Complete Search, Divide & Conquer, Greedy Section 3.2-3.4; 8.2
04 Dynamic Programming 1 (Basic ideas) Section 3.5; 4.7.1
05 Dynamic Programming 2 (More techniques) Section 5.4; 5.6; 6.5; 8.3
06 Mid-Semester Team Contest Chapter 1 - 4; parts of Ch 9
- Mid-Semester Break (homework)
07 Graph 1 (Network Flow) Section 4.6; parts of Ch 9
08 Graph 2 (Matching) Section 4.7.4; parts of Ch 9
09 Mathematics (Overview) Chapter 5
10 String Processing (Basic skills, Suffix Array) Chapter 6
11 (Computational) Geometry (Libraries) Chapter 7
12 More Advanced Topics Section 8.4; parts of Ch 9
13 Final Team Contest Chapter 1-9 and maybe more
- No final exam -

Table 2: Lesson Plan of Steven’s CS3233

For Data Structures and Algorithms Courses


The contents of this book have been expanded in this edition so that the first four chapters of
this book are more accessible to first year Computer Science students. Topics and exercises
that we have found to be relatively difficult and thus unnecessarily discouraging for first
timers have been moved to the now bulkier Chapter 8 or to the new Chapter 9. This way,
students who are new to Computer Science will perhaps not feel overly intimidated when
they peruse the first four chapters.
Chapter 2 has received a major update. Previously, Section 2.2 was just a casual list
of classical data structures and their libraries. This time, we have expanded the write-up
and added lots of written exercises so that this book can also be used to support a Data
Structures course, especially in the terms of implementation details.
The four problem solving paradigms discussed in Chapter 3 appear frequently in typical
Algorithms courses. The text in this chapter has been expanded and edited to help new
Computer Science students.

x
CONTENTS 
c Steven & Felix

Parts of Chapter 4 can also be used as a supplementary reading or implementation guide


to enhance a Discrete Mathematics [57, 15] or a basic Algorithms course. We have also
provide some new insights on viewing Dynamic Programming techniques as algorithms on
DAGs. Such discussion is currently still regrettably uncommon in many Computer Science
textbooks.

To All Readers
Due to its diversity of coverage and depth of discussion, this book is not meant to be
read once, but several times. There are many written (≈ 238) and programming exercises
(≈ 1675) listed and spread across almost every section. You can skip these exercises at
first if the solution is too difficult or requires further knowledge and technique, and revisit
them after studying other chapters of this book. Solving these exercises will strengthen
your understanding of the concepts taught in this book as they usually involve interesting
applications, twists or variants of the topic being discussed. Make an effort to attempt
them—time spent solving these problems will definitely not be wasted.
We believe that this book is and will be relevant to many university and high school
students. Programming competitions such as the ICPC and IOI are here to stay, at least
for many years ahead. New students should aim to understand and internalize the basic
knowledge presented in this book before hunting for further challenges. However, the term
‘basic’ might be slightly misleading—please check the table of contents to understand what
we mean by ‘basic’.
As the title of this book may imply, the purpose of this book is clear: We aim to
improve everyone’s programming abilities and thus increase the lower bound of programming
competitions like the ICPC and IOI in the future. With more contestants mastering the
contents of this book, we hope that the year 2010 (when the first edition of this book was
published) will be a watershed marking an accelerated improvement in the standards of
programming contests. We hope to help more teams solve more (≥ 2) problems in future
ICPCs and help more contestants to achieve greater (≥ 200) scores in future IOIs. We also
hope to see many ICPC and IOI coaches around the world (especially in South East Asia)
adopt this book for the aid it provides in mastering topics that students cannot do without
in competitive programming contests. If such a proliferation of the required ‘lower-bound’
knowledge for competitive programming is achieved, then this book’s primary objective of
advancing the level of human knowledge will have been fulfilled, and we, as the authors of
this book, will be very happy indeed.

Convention
There are lots of C/C++ code and also some Java code (especially in Section 5.3) included
in this book. If they appear, they will be typeset in this monospace font.
For the C/C++ code in this book, we have adopted the frequent use of typedefs and
macros—features that are commonly used by competitive programmers for convenience,
brevity, and coding speed. However, we cannot use similar techniques for Java as it does
not contain similar or analogous features. Here are some examples of our C/C++ code
shortcuts:

// Suppress some compilation warning messages (only for VC++ users)


#define _CRT_SECURE_NO_DEPRECATE

xi
CONTENTS 
c Steven & Felix

// Shortcuts for "common" data types in contests


typedef long long ll; // comments that are mixed in with code
typedef pair<int, int> ii; // are aligned to the right like this
typedef vector<ii> vii;
typedef vector<int> vi;
#define INF 1000000000 // 1 billion, safer than 2B for Floyd Warshall’s

// Common memset settings


//memset(memo, -1, sizeof memo); // initialize DP memoization table with -1
//memset(arr, 0, sizeof arr); // to clear array of integers

// We have abandoned the use of "REP" and "TRvii" since the second edition
// in order to reduce the confusion encountered by new programmers
The following shortcuts are frequently used in both our C/C++ and Java code:
// ans = a ? b : c; // to simplify: if (a) ans = b; else ans = c;
// ans += val; // to simplify: ans = ans + val; and its variants
// index = (index + 1) % n; // index++; if (index >= n) index = 0;
// index = (index + n - 1) % n; // index--; if (index < 0) index = n - 1;
// int ans = (int)((double)d + 0.5); // for rounding to nearest integer
// ans = min(ans, new_computation); // min/max shortcut
// alternative form but not used in this book: ans <?= new_computation;
// some code use short circuit && (AND) and || (OR)

Problem Categorization
As of 24 May 2013, Steven and Felix—combined—have solved 1903 UVa problems (≈ 46.45%
of the entire UVa problemset). About ≈ 1675 of them are discussed and categorized in this
book. Since late 2011, some Live Archive problems have also been integrated in the UVa
Online Judge. In this book, we use both problem numberings, but the primary sort key used
in the index section of this book is the UVa problem number.
These problems are categorized according to a ‘load balancing’ scheme: If a problem can
be classified into two or more categories, it will be placed in the category with a lower number
of problems. This way, you may find that some problems have been ‘wrongly’ categorized,
where the category that it appears in might not match the technique that you have used to
solve it. We can only guarantee that if you see problem X in category Y, then you know
that we have managed to solve problem X with the technique mentioned in the section that
discusses category Y.
We have also limited each category to at most 25 (TWENTY FIVE) problems, splitting
them into separate categories when needed.
If you need hints for any of the problems (that we have solved), flip to the handy index
at the back of this book instead of flipping through each chapter—it might save you some
time. The index contains a list of UVa/LA problems, ordered by their problem number (do
a binary search!) and augmented by the pages that contain discussion of said problems (and
the data structures and/or algorithms required to solve that problem). In the third edition,
we allow the hints to span more than one line so that they can be more meaningful.
Utilize this categorization feature for your training! Solving at least a few problems
from each category (especially the ones we have highlighted as must try *) is a great way
to diversify your problem solving skillset. For conciseness, we have limited ourselves to a
maximum of 3 highlights per category.

xii
CONTENTS 
c Steven & Felix

Changes for the Second Edition


There are substantial changes between the first and the second edition of this book. As
the authors, we have learned a number of new things and solved hundreds of programming
problems during the one year gap between these two editions. We also have received feedback
from readers, especially from Steven’s CS3233 class Sem 2 AY2010/2011 students, and have
incorporated these suggestions in the second edition.
Here is a summary of the important changes for the second edition:

• The first noticeable change is the layout. We now have a greater information density
on each page. The 2nd edition uses single line spacing instead of the 1.5 line spacing
used in the 1st edition. The positioning of small figures is also enhanced so that we
have a more compact layout. This is to avoid increasing the number of pages by too
much while still adding more content.

• Some minor bugs in our code examples (both the ones displayed in the book and the
soft copies provided in the companion web site) have been fixed. All code samples now
have much more meaningful comments to aid in comprehension.

• Several language-related issues (typographical, grammatical or stylistic) have been


corrected.

• Besides enhancing the discussion of many data structures, algorithms, and program-
ming problems, we have also added these new materials in each chapter:

1. Many new Ad Hoc problems to kick start this book (Section 1.4).
2. A lightweight set of Boolean (bit-manipulation) techniques (Section 2.2), Implicit
Graphs (Section 2.4.1), and Fenwick Tree data structures (Section 2.4.4).
3. More DP: A clearer explanation of bottom-up DP, the O(n log k) solution for the
LIS problem, the 0-1 Knapsack/Subset Sum, and DP TSP (using the bitmask
technique) (Section 3.5.2).
4. A reorganization of the graph material into: Graph Traversal (both DFS and
BFS), Minimum Spanning Tree, Shortest Paths (Single-Source and All-Pairs),
Maximum Flow, and Special Graphs. New topics include Prim’s MST algorithm,
a discussion of DP as a traversal on implicit DAGs (Section 4.7.1), Eulerian
Graphs (Section 4.7.3), and the Augmenting Path algorithm (Section 4.7.4).
5. A reorganization of mathematical techniques (Chapter 5) into: Ad Hoc, Java
BigInteger, Combinatorics, Number Theory, Probability Theory, Cycle-Finding,
Game Theory (new), and Powers of a (Square) Matrix (new). Each topic has
been rewritten for clarity.
6. Basic string processing skills (Section 6.2), more string-related problems (Section
6.3), including string matching (Section 6.4), and an enhanced Suffix Tree/Array
explanation (Section 6.6).
7. More geometry libraries (Chapter 7), especially on points, lines and polygons.
8. A new Chapter 8, which contains discussion on problem decomposition, advanced
search techniques (A*, Depth Limited Search, Iterative Deepening, IDA*), ad-
vanced DP techniques (more bitmask techniques, the Chinese Postman Problem,
a compilation of common DP states, a discussion of better DP states, and some
harder DP problems).

xiii
CONTENTS 
c Steven & Felix

• Many existing figures in this book have been redrawn and enhanced. Many new figures
have been added to help explain the concepts more clearly.

• The first edition is mainly written using from the viewpoint of the ICPC contestant and
C++ programmer. The second edition is written to be more balanced and includes
the IOI perspective. Java support is also strongly enhanced in the second edition.
However, we do not support any other programming languages as of yet.

• Steven’s ‘Methods to Solve’ website has now been fully integrated in this book in the
form of ‘one liner hints’ for each problem and the useful problem index at the back
of this book. Now, reaching 1000 problems solved in UVa online judge is no longer
a wild dream (we believe that this feat is doable by a serious 4-year CS university
undergraduate).

• Some examples in the first edition use old programming problems. In the second
edition, these examples have been replaced/added with newer examples.

• ≈ 600 more programming exercises from the UVa Online Judge and Live Archive have
been solved by Steven & Felix and added to this book. We have also added many more
written exercises throughout the book with hints/short solutions as appendices.

• Short profiles of data structure/algorithm inventors have been adapted from Wikipedia
[71] or other sources for this book. It is nice to know a little bit more about these
inventors.

Changes for the Third Edition


We gave ourselves two years (skipping 2012) to prepare a substantial number of improvements
and additional materials for the third edition of this book. Here is the summary of the
important changes for the third edition:

• The third edition now uses a slightly larger font size (12 pt) compared to second edition
(11 pt), a 9 percent increase. Hopefully many readers will find the text more readable
this time. We also use larger figures. These decisions, however, have increased the
number of pages and rendered the book thicker. We have also adjusted the left/right
margin in odd/even pages to increase readability.

• The layout has been changed to start almost every section on a new page. This is to
make the layout far easier to manage.

• We have added many more written exercises throughout the book and classifed them
into non-starred (for self-checking purposes; hints/solutions are at the back of each
chapter) and starred * versions (for extra challenges; no solution is provided). The
written exercises have been placed close to the relevant discussion in the body text.

• ≈ 477 more programming exercises from the UVa Online Judge and Live Archive have
been solved by Steven & Felix and consequently added to this book. We thus have
maintained a sizeable ≈ 50% (to be precise, ≈ 46.45%) coverage of UVa Online Judge
problems even as the judge has grown in the same period of time. These newer problems
have been listed in an italic font. Some of the newer problems have replaced older ones
as the must try problems. All programming exercises are now always placed at the
end of a section.

xiv
CONTENTS 
c Steven & Felix

• We now have proof that capable CS students can achieve ≥ 500 AC problems (from 0)
in the UVa Online Judge in just one University semester (4 months) with this book.
• The new (or revised) materials, chapter by chapter:

1. Chapter 1 contains a gentler introduction for readers who are new to competitive
programming. We have elaborated on stricter Input/Output (I/O) formats in
typical programming problems and common routines for dealing with them.
2. We add one more linear data structure: ‘deque’ in Section 2.2. Chapter 2 now
contains a more detailed discussion of almost all data structures discussed in this
chapter, especially Section 2.3 and 2.4.
3. In Chapter 3, we have a more detailed discussions of various Complete Search
techniques: Nested loops, generating subsets/permutations iteratively, and recur-
sive backtracking. New: An interesting trick to write and print Top-Down DP
solutions, Discussion of Kadane’s algorithm for Max 1D Range Sum.
4. In Chapter 4, we have revised white/gray/black labels (legacy from [7]) to their
standard nomenclature, renaming ‘max flow’ to ‘network flow’ in the process. We
have also referred to the algorithm author’s actual scientific paper for a better
understanding of the original ideas of the algorithm. We now have new diagrams
of the implicit DAG in classical DP problems found in Section 3.5.
5. Chapter 5: We have included greater coverage of Ad Hoc mathematics prob-
lems, a discussion of an interesting Java BigInteger operation: isProbablePrime,
added/expanded several commonly used Combinatorics formulae and modified
sieve algorithms, expanded/revised sections on Probability Theory (Section 5.6),
Cycle-finding (Section 5.7), and Game Theory (Section 5.8).
6. Chapter 6: We rewrite Section 6.6 to have a better explanation of Suffix Trie/Tree/
Array by reintroducing the concept of terminating character.
7. Chapter 7: We trim this chapter into two core sections and improve the library
code quality.
8. Chapter 8: The harder topics that were listed in Chapter 1-7 in the 2nd edition
have now been relocated to Chapter 8 (or Chapter 9 below). New: Discussion
of harder backtracking routine, State-Space search, meet in the middle, trick of
using balanced BST as memo table, and a more comprehensive section about
problem decomposition.
9. New Chapter 9: Various rare topics that appear once a while in programming
contests have been added. Some of them are easy, but many of them are hard
and can be somewhat important score determinants in programming contests.

Supporting Websites
This book has an official companion web site at sites.google.com/site/stevenhalim,
from which you can obtain a soft copy of sample source code and the (public/simpler version)
of the) PDF slides used in Steven’s CS3233 classes.
All programming exercises in this book are integrated in the uhunt.felix-halim.net tool
and can be found in the UVa Online Judge at uva.onlinejudge.org
New in the third edition: Many algorithms now have interactive visualizations at:
www.comp.nus.edu.sg/~stevenha/visualization

xv
CONTENTS 
c Steven & Felix

Acknowledgments for the First Edition


From Steven: I want to thank

• God, Jesus Christ, and the Holy Spirit, for giving me talent and passion in competitive
programming.

• my lovely wife, Grace Suryani, for allowing me to spend our precious time for this
project.

• my younger brother and co-author, Felix Halim, for sharing many data structures,
algorithms, and programming tricks to improve the writing of this book.

• my father Lin Tjie Fong and mother Tan Hoey Lan for raising us and encouraging us
to do well in our study and work.

• the School of Computing, National University of Singapore, for employing me and


allowing me to teach the CS3233 - ‘Competitive Programming’ module from which
this book was born.

• NUS/ex-NUS professors/lecturers who have shaped my competitive programming and


coaching skills: Prof Andrew Lim Leong Chye, Assoc Prof Tan Sun Teck, Aaron Tan
Tuck Choy, Assoc Prof Sung Wing Kin, Ken, Dr Alan Cheng Holun.

• my friend Ilham Winata Kurnia for proof reading the manuscript of the first edition.

• fellow Teaching Assistants of CS3233 and ACM ICPC Trainers @ NUS: Su Zhan, Ngo
Minh Duc, Melvin Zhang Zhiyong, Bramandia Ramadhana.

• my CS3233 students in Sem2 AY2008/2009 who inspired me to come up with the


lecture notes and students in Sem2 AY2009/2010 who verified the content of the first
edition of this book and gave the initial Live Archive contribution

Acknowledgments for the Second Edition


From Steven: Additionally, I also want to thank

• the first ≈ 550 buyers of the 1st edition as of 1 August 2011 (this number is no longer
updated). Your supportive responses encourage us!

xvi
CONTENTS 
c Steven & Felix

• a fellow Teaching Assistant of CS3233 @ NUS: Victor Loh Bo Huai.

• my CS3233 students in Sem2 AY2010/2011 who contributed in both technical and


presentation aspects of the second edition, in alphabetical order: Aldrian Obaja Muis,
Bach Ngoc Thanh Cong, Chen Juncheng, Devendra Goyal, Fikril Bahri, Hassan Ali
Askari, Harta Wijaya, Hong Dai Thanh, Koh Zi Chun, Lee Ying Cong, Peter Phandi,
Raymond Hendy Susanto, Sim Wenlong Russell, Tan Hiang Tat, Tran Cong Hoang,
Yuan Yuan, and one other student who prefers to be anonymous.

• the proof readers: Seven of CS3233 students above (underlined) plus Tay Wenbin.

• Last but not least, I want to re-thank my wife, Grace Suryani, for letting me do another
round of tedious book editing process while she was pregnant with our first baby: Jane
Angelina Halim.

Acknowledgments for the Third Edition


From Steven: Again, I want to thank

• the ≈ 2000 buyers of the 2nd edition as of 24 May 2013 (this number is no longer
updated). Thanks :).

xvii
CONTENTS 
c Steven & Felix

• fellow Teaching Assistant of CS3233 @ NUS in the past two years: Harta Wijaya,
Trinh Tuan Phuong, and Huang Da.

• my CS3233 students in Sem2 AY2011/2012 who contributed in both technical and


presentation aspects of the second edition of this book, in alphabetical order: Cao
Sheng, Chua Wei Kuan, Han Yu, Huang Da, Huynh Ngoc Tai, Ivan Reinaldo, John
Goh Choo Ern, Le Viet Tien, Lim Zhi Qin, Nalin Ilango, Nguyen Hoang Duy, Nguyen
Phi Long, Nguyen Quoc Phong, Pallav Shinghal, Pan Zhengyang, Pang Yan Han, Song
Yangyu, Tan Cheng Yong Desmond, Tay Wenbin, Yang Mansheng, Zhao Yang, Zhou
Yiming, and two other students who prefer to be anonymous.

• the proof readers: Six of CS3233 students in Sem2 AY2011/2012 (underlined) and
Hubert Teo Hua Kian.

• my CS3233 students in Sem2 AY2012/2013 who contributed in both technical and


presentation aspects of the second edition of this book, in alphabetical order: Arnold
Christopher Koroa, Cao Luu Quang, Lim Puay Ling Pauline, Erik Alexander Qvick
Faxaa, Jonathan Darryl Widjaja, Nguyen Tan Sy Nguyen, Nguyen Truong Duy, Ong
Ming Hui, Pan Yuxuan, Shubham Goyal, Sudhanshu Khemka, Tang Binbin, Trinh
Ngoc Khanh, Yao Yujian, Zhao Yue, and Zheng Naijia.

• the NUS Centre for Development of Teaching and Learning (CDTL) for giving the
initial funding to build the algorithm visualization website.

• my wife Grace Suryani and my daughter Jane Angelina for your love in our family.

To a better future of humankind,


Steven and Felix Halim
Singapore, 24 May 2013

Copyright
No part of this book may be reproduced or transmitted in any form or by any means, elec-
tronically or mechanically, including photocopying, scanning, uploading to any information
storage and retrieval system.

xviii
CONTENTS 
c Steven & Felix

Authors’ Profiles
Steven Halim, PhD1
[email protected]
Steven Halim is currently a lecturer in
School of Computing, National University
of Singapore (SoC, NUS). He teaches sev-
eral programming courses in NUS, ranging
from basic programming methodology, inter-
mediate data structures and algorithms, and
also the ‘Competitive Programming’ module
that uses this book. He is the coach of both
the NUS ACM ICPC teams and the Singa-
pore IOI team. He participated in several
ACM ICPC Regional as student (Singapore
2001, Aizu 2003, Shanghai 2004). So far,
he and other trainers @ NUS have success-
fully groomed two ACM ICPC World Final-
ist teams (2009-2010; 2012-2013) as well as
two gold, six silver, and seven bronze IOI
medalists (2009-2012).
Steven is happily married with Grace
Suryani Tioso and currently has one daugh-
ter: Jane Angelina Halim.

Felix Halim, PhD2


[email protected]
Felix Halim now holds a PhD degree from SoC, NUS. In terms of
programming contests, Felix has a much more colourful reputation
than his older brother. He was IOI 2002 contestant (representing
Indonesia). His ICPC teams (at that time, Bina Nusantara Univer-
sity) took part in ACM ICPC Manila Regional 2003-2004-2005 and
obtained rank 10th, 6th, and 10th respectively. Then, in his final
year, his team finally won ACM ICPC Kaohsiung Regional 2006
and thus became ACM ICPC World Finalists @ Tokyo 2007 (44th
place). Today, he actively joins TopCoder Single Round Matches
and his highest rating is a yellow coder. He now works at Google,
Mountain View, United States of America.

1
PhD Thesis: “An Integrated White+Black Box Approach for Designing and Tuning Stochastic Local
Search Algorithms”, 2009.
2
PhD Thesis: “Solving Big Data Problems: from Sequences to Tables and Graphs”, 2012.

xix
CONTENTS 
c Steven & Felix

Abbreviations LSB : Least Significant Bit

MCBM : Max Cardinality Bip Matching


MCM : Matrix Chain Multiplication
MCMF : Min-Cost Max-Flow
A* : A Star
MIS : Maximum Independent Set
ACM : Assoc of Computing Machinery
MLE : Memory Limit Exceeded
AC : Accepted
MPC : Minimum Path Cover
APSP : All-Pairs Shortest Paths
MSB : Most Significant Bit
AVL : Adelson-Velskii Landis (BST)
MSSP : Multi-Sources Shortest Paths
BNF : Backus Naur Form MST : Minimum Spanning Tree
BFS : Breadth First Search MWIS : Max Weighted Independent Set
BI : Big Integer MVC : Minimum Vertex Cover
BIT : Binary Indexed Tree
OJ : Online Judge
BST : Binary Search Tree
PE : Presentation Error
CC : Coin Change
CCW : Counter ClockWise RB : Red-Black (BST)
CF : Cumulative Frequency RMQ : Range Min (or Max) Query
CH : Convex Hull RSQ : Range Sum Query
CS : Computer Science RTE : Run Time Error
CW : ClockWise
SSSP : Single-Source Shortest Paths
DAG : Directed Acyclic Graph SA : Suffix Array
DAT : Direct Addressing Table SPOJ : Sphere Online Judge
D&C : Divide and Conquer ST : Suffix Tree
DFS : Depth First Search STL : Standard Template Library
DLS : Depth Limited Search
DP : Dynamic Programming TLE : Time Limit Exceeded
DS : Data Structure
USACO : USA Computing Olympiad
ED : Edit Distance UVa : University of Valladolid [47]

FIFO : First In First Out WA : Wrong Answer


FT : Fenwick Tree WF : World Finals

GCD : Greatest Common Divisor

ICPC : Intl Collegiate Prog Contest


IDS : Iterative Deepening Search
IDA* : Iterative Deepening A Star
IOI : Intl Olympiad in Informatics
IPSC : Internet Problem Solving Contest

LA : Live Archive [33]


LCA : Lowest Common Ancestor
LCM : Least Common Multiple
LCP : Longest Common Prefix
LCS1 : Longest Common Subsequence
LCS2 : Longest Common Substring
LIFO : Last In First Out
LIS : Longest Increasing Subsequence
LRS : Longest Repeated Substring
xx
List of Tables

1 Not in IOI Syllabus [20] Yet . . . . . . . . . . . . . . . . . . . . . . . . . . . ix


2 Lesson Plan of Steven’s CS3233 . . . . . . . . . . . . . . . . . . . . . . . . . x

1.1 Recent ACM ICPC (Asia) Regional Problem Types . . . . . . . . . . . . . . 5


1.2 Problem Types (Compact Form) . . . . . . . . . . . . . . . . . . . . . . . . 5
1.3 Exercise: Classify These UVa Problems . . . . . . . . . . . . . . . . . . . . . 6
1.4 Rule of Thumb for the ‘Worst AC Algorithm’ for various input size n . . . . 8

2.1 Example of a Cumulative Frequency Table . . . . . . . . . . . . . . . . . . . 59


2.2 Comparison Between Segment Tree and Fenwick Tree . . . . . . . . . . . . . 63

3.1 Running Bisection Method on the Example Function . . . . . . . . . . . . . 86


3.2 DP Decision Table . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
3.3 UVa 108 - Maximum Sum . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
3.4 Summary of Classical DP Problems in this Section . . . . . . . . . . . . . . 114
3.5 Comparison of Problem Solving Techniques (Rule of Thumb only) . . . . . . 120

4.1 List of Important Graph Terminologies . . . . . . . . . . . . . . . . . . . . . 121


4.2 Graph Traversal Algorithm Decision Table . . . . . . . . . . . . . . . . . . . 135
4.3 Floyd Warshall’s DP Table . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
4.4 SSSP/APSP Algorithm Decision Table . . . . . . . . . . . . . . . . . . . . . 161
4.5 Characters Used in UVa 11380 . . . . . . . . . . . . . . . . . . . . . . . . . . 169

5.1 List of some mathematical terms discussed in this chapter . . . . . . . . . . 191


5.2 Part 1: Finding kλ, f (x) = (3 × x + 1)%4, x0 = 7 . . . . . . . . . . . . . . . 224
5.3 Part 2: Finding μ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
5.4 Part 3: Finding λ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224

6.1 L/R: Before/After Sorting; k = 1; the initial sorted order appears . . . . . . 255
6.2 L/R: Before/After Sorting; k = 2; ‘GATAGACA’ and ‘GACA’ are swapped . . . 256
6.3 Before/After sorting; k = 4; no change . . . . . . . . . . . . . . . . . . . . . 257
6.4 String Matching using Suffix Array . . . . . . . . . . . . . . . . . . . . . . . 260
6.5 Computing the LCP given the SA of T = ‘GATAGACA$’ . . . . . . . . . . . . 261
6.6 The Suffix Array, LCP, and owner of T = ‘GATAGACA$CATA#’ . . . . . . . . 262

9.1 The Reduction from LCA to RMQ . . . . . . . . . . . . . . . . . . . . . . . 360


9.2 Examples of Infix, Prefix, and Postfix expressions . . . . . . . . . . . . . . . 376
9.3 Example of a Postfix Calculation . . . . . . . . . . . . . . . . . . . . . . . . 376
9.4 Example of an Execution of Shunting yard Algorithm . . . . . . . . . . . . . 377

xxi
List of Figures

1.1 Illustration of UVa 10911 - Forming Quiz Teams . . . . . . . . . . . . . . . . 2


1.2 UVa Online Judge and ACM ICPC Live Archive . . . . . . . . . . . . . . . . 15
1.3 USACO Training Gateway and Sphere Online Judge . . . . . . . . . . . . . 16
1.4 Some references that inspired the authors to write this book . . . . . . . . . 31

2.1 Bitmask Visualization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36


2.2 Examples of BST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
2.3 (Max) Heap Visualization . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
2.4 Graph Data Structure Visualization . . . . . . . . . . . . . . . . . . . . . . . 49
2.5 Implicit Graph Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
2.6 unionSet(0, 1) → (2, 3) → (4, 3) and isSameSet(0, 4) . . . . . . . 53
2.7 unionSet(0, 3) → findSet(0) . . . . . . . . . . . . . . . . . . . . . . . . . 53
2.8 Segment Tree of Array A = {18, 17, 13, 19, 15, 11, 20} and RMQ(1, 3) . . . 56
2.9 Segment Tree of Array A = {18, 17, 13, 19, 15, 11, 20} and RMQ(4, 6) . . . 56
2.10 Updating Array A to {18, 17, 13, 19, 15, 99, 20} . . . . . . . . . . . . . . . 57
2.11 Example of rsq(6) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
2.12 Example of rsq(3) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
2.13 Example of adjust(5, 1) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61

3.1 8-Queens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
3.2 UVa 10360 [47] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
3.3 My Ancestor (all 5 root-to-leaf paths are sorted) . . . . . . . . . . . . . . . . 85
3.4 Visualization of UVa 410 - Station Balance . . . . . . . . . . . . . . . . . . . 90
3.5 UVa 410 - Observations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
3.6 UVa 410 - Greedy Solution . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
3.7 UVa 10382 - Watering Grass . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
3.8 Bottom-Up DP (columns 21 to 200 are not shown) . . . . . . . . . . . . . . 100
3.9 Longest Increasing Subsequence . . . . . . . . . . . . . . . . . . . . . . . . . 106
3.10 Coin Change . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
3.11 A Complete Graph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
3.12 Cutting Sticks Illustration . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113

4.1 Sample Graph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122


4.2 UVa 11902 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
4.3 Example Animation of BFS . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
4.4 An Example of DAG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
4.5 Animation of DFS when Run on the Sample Graph in Figure 4.1 . . . . . . 129
4.6 Introducing two More DFS Attributes: dfs num and dfs low . . . . . . . . . 131
4.7 Finding Articulation Points with dfs num and dfs low . . . . . . . . . . . . 131
4.8 Finding Bridges, also with dfs num and dfs low . . . . . . . . . . . . . . . . 132
4.9 An Example of a Directed Graph and its SCCs . . . . . . . . . . . . . . . . 134

xxii
LIST OF FIGURES 
c Steven & Felix

4.10 Example of an MST Problem . . . . . . . . . . . . . . . . . . . . . . . . . . 138


4.11 Animation of Kruskal’s Algorithm for an MST Problem . . . . . . . . . . . . 139
4.12 Animation of Prim’s Algorithm for the same graph as in Figure 4.10—left . . 140
4.13 From left to right: MST, ‘Maximum’ ST, ‘Minimum’ SS, MS ‘Forest’ . . . . 141
4.14 Second Best ST (from UVa 10600 [47]) . . . . . . . . . . . . . . . . . . . . . 142
4.15 Finding the Second Best Spanning Tree from the MST . . . . . . . . . . . . 142
4.16 Minimax (UVa 10048 [47]) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
4.17 Dijkstra Animation on a Weighted Graph (from UVa 341 [47]) . . . . . . . . 149
4.18 -ve Weight . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
4.19 Bellman Ford’s can detect the presence of negative cycle (from UVa 558 [47]) 151
4.20 Floyd Warshall’s Explanation 1 . . . . . . . . . . . . . . . . . . . . . . . . . 156
4.21 Floyd Warshall’s Explanation 2 . . . . . . . . . . . . . . . . . . . . . . . . . 156
4.22 Floyd Warshall’s Explanation 3 . . . . . . . . . . . . . . . . . . . . . . . . . 157
4.23 Floyd Warshall’s Explanation 4 . . . . . . . . . . . . . . . . . . . . . . . . . 157
4.24 Max Flow Illustration (UVa 820 [47] - ICPC World Finals 2000 Problem E) . 163
4.25 Ford Fulkerson’s Method Implemented with DFS Can Be Slow . . . . . . . . 164
4.26 What are the Max Flow value of these three residual graphs? . . . . . . . . . 165
4.27 Residual Graph of UVa 259 [47] . . . . . . . . . . . . . . . . . . . . . . . . . 166
4.28 Vertex Splitting Technique . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
4.29 Some Test Cases of UVa 11380 . . . . . . . . . . . . . . . . . . . . . . . . . . 168
4.30 Flow Graph Modeling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
4.31 Special Graphs (L-to-R): DAG, Tree, Eulerian, Bipartite Graph . . . . . . . 171
4.32 The Longest Path on this DAG . . . . . . . . . . . . . . . . . . . . . . . . . 172
4.33 Example of Counting Paths in DAG - Bottom-Up . . . . . . . . . . . . . . . 172
4.34 Example of Counting Paths in DAG - Top-Down . . . . . . . . . . . . . . . . 173
4.35 The Given General Graph (left) is Converted to DAG . . . . . . . . . . . . . 174
4.36 The Given General Graph/Tree (left) is Converted to DAG . . . . . . . . . . 175
4.37 Coin Change as Shortest Paths on DAG . . . . . . . . . . . . . . . . . . . . 176
4.38 0-1 Knapsack as Longest Paths on DAG . . . . . . . . . . . . . . . . . . . . 177
4.39 UVa 10943 as Counting Paths in DAG . . . . . . . . . . . . . . . . . . . . . 177
4.40 A: SSSP (Part of APSP); B1-B2: Diameter of Tree . . . . . . . . . . . . . . 179
4.41 Eulerian . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
4.42 Bipartite Matching problem can be reduced to a Max Flow problem . . . . . 181
4.43 MCBM Variants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
4.44 Augmenting Path Algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . 183

5.1 Left: Triangulation of a Convex Polygon, Right: Monotonic Paths . . . . . . 206


5.2 Decision Tree for an instance of ‘Euclid’s Game’ . . . . . . . . . . . . . . . . 226
5.3 Partial Decision Tree for an instance of ‘A multiplication game’ . . . . . . . 227

6.1 Example: A = ‘ACAATCC’ and B = ‘AGCATGC’ (alignment score = 7) . . . . 246


6.2 Suffix Trie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
6.3 Suffixes, Suffix Trie, and Suffix Tree of T = ‘GATAGACA$’ . . . . . . . . . . . 250
6.4 String Matching of T = ‘GATAGACA$’ with Various Pattern Strings . . . . . 251
6.5 Longest Repeated Substring of T = ‘GATAGACA$’ . . . . . . . . . . . . . . . 252
6.6 Generalized ST of T1 = ‘GATAGACA$’ and T2 = ‘CATA#’ and their LCS . . 253
6.7 Sorting the Suffixes of T = ‘GATAGACA$’ . . . . . . . . . . . . . . . . . . . . 254
6.8 Suffix Tree and Suffix Array of T = ‘GATAGACA$’ . . . . . . . . . . . . . . . 254

7.1 Rotating point (10, 3) by 180 degrees counter clockwise around origin (0, 0) 272
7.2 Distance to Line (left) and to Line Segment (middle); Cross Product (right) 274

xxiii
LIST OF FIGURES 
c Steven & Felix

7.3 Circles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 277


7.4 Circle Through 2 Points and Radius . . . . . . . . . . . . . . . . . . . . . . . 278
7.5 Triangles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
7.6 Incircle and Circumcircle of a Triangle . . . . . . . . . . . . . . . . . . . . . 280
7.7 Quadrilaterals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
7.8 Left: Convex Polygon, Right: Concave Polygon . . . . . . . . . . . . . . . . 286
7.9 Top Left: inside, Top Right: also inside, Bottom: outside . . . . . . . . . . . 287
7.10 Left: Before Cut, Right: After Cut . . . . . . . . . . . . . . . . . . . . . . . 288
7.11 Rubber Band Analogy for Finding Convex Hull . . . . . . . . . . . . . . . . 289
7.12 Sorting Set of 12 Points by Their Angles w.r.t a Pivot (Point 0) . . . . . . . 290
7.13 The Main Part of Graham’s Scan algorithm . . . . . . . . . . . . . . . . . . 291
7.14 Explanation for Circle Through 2 Points and Radius . . . . . . . . . . . . . 295

8.1 5 Queens problem: The initial state . . . . . . . . . . . . . . . . . . . . . . . 300


8.2 5 Queens problem: After placing the first queen . . . . . . . . . . . . . . . . 301
8.3 5 Queens problem: After placing the second queen . . . . . . . . . . . . . . . 301
8.4 5 Queens problem: After placing the third queen . . . . . . . . . . . . . . . . 302
8.5 N-Queens, after placing the fourth and the fifth queens . . . . . . . . . . . . 302
8.6 Visualization of UVa 1098 - Robots on Ice . . . . . . . . . . . . . . . . . . . 304
8.7 Case 1: Example when s is two steps away from t . . . . . . . . . . . . . . . 307
8.8 Case 2: Example when s is four steps away from t . . . . . . . . . . . . . . . 307
8.9 Case 3: Example when s is five steps away from t . . . . . . . . . . . . . . . 307
8.10 15 Puzzle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
8.11 The Descent Path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315
8.12 Illustration for ACM ICPC WF2010 - J - Sharing Chocolate . . . . . . . . . 317
8.13 Athletics Track (from UVa 11646) . . . . . . . . . . . . . . . . . . . . . . . . 321
8.14 Illustration for ACM ICPC WF2009 - A - A Careful Approach . . . . . . . . 326

9.1 The Implication Graph of Example 1 (Left) and Example 2 (Right) . . . . . 336
9.2 The Standard TSP versus Bitonic TSP . . . . . . . . . . . . . . . . . . . . . 339
9.3 An Example of Chinese Postman Problem . . . . . . . . . . . . . . . . . . . 342
9.4 The Four Common Variants of Graph Matching in Programming Contests . 349
9.5 A Sample Test Case of UVa 10746: 3 Matchings with Min Cost = 40 . . . . 350
9.6 L: Sphere, M: Hemisphere and Great-Circle, R: gcDistance (Arc A-B) . . . . 352
9.7 Comparison Between Max Independent Paths vs Max Edge-Disjoint Paths . 354
9.8 An example of a rooted tree T with n = 10 vertices . . . . . . . . . . . . . . 359
9.9 The Magic Square Construction Strategy for Odd n . . . . . . . . . . . . . . 361
9.10 An Example of Min Cost Max Flow (MCMF) Problem (UVa 10594 [47]) . . 369
9.11 Min Path Cover on DAG (from UVa 1201 [47]) . . . . . . . . . . . . . . . . . 370
9.12 Example of an AVL Tree Deletion (Delete 7) . . . . . . . . . . . . . . . . . . 382
9.13 Explanation of RMQ(i, j) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 388

A.1 Steven’s statistics as of 24 May 2013 . . . . . . . . . . . . . . . . . . . . . . 393


A.2 Hunting the next easiest problems using ‘dacu’ . . . . . . . . . . . . . . . . . 394
A.3 We can rewind past contests with ‘virtual contest’ . . . . . . . . . . . . . . . 394
A.4 The programming exercises in this book are integrated in uHunt . . . . . . . 395
A.5 Steven’s & Felix’s progress in UVa online judge (2000-present) . . . . . . . . 395
A.6 Andrian, Felix, and Andoko Won ACM ICPC Kaohsiung 2006 . . . . . . . . 395

xxiv
Chapter 1

Introduction

I want to compete in ACM ICPC World Finals!


— A dedicated student

1.1 Competitive Programming


The core directive in ‘Competitive Programming’ is this: “Given well-known Computer
Science (CS) problems, solve them as quickly as possible!”.
Let’s digest the terms one by one. The term ‘well-known CS problems’ implies that in
competitive programming, we are dealing with solved CS problems and not research problems
(where the solutions are still unknown). Some people (at least the problem author) have
definitely solved these problems before. To ‘solve them’ implies that we1 must push our CS
knowledge to a certain required level so that we can produce working code that can solve
these problems too—at least in terms of getting the same output as the problem author
using the problem author’s secret2 test data within the stipulated time limit. The need to
solve the problem ‘as quickly as possible’ is where the competitive element lies—speed is a
very natural goal in human behavior.

An illustration: UVa Online Judge [47] Problem Number 10911 (Forming Quiz Teams).

Abridged Problem Description:


Let (x, y) be the coordinates of a student’s house on a 2D plane. There are 2N students
and we want to pair them into N groups. Let di be the distance  between the houses
of 2 students in group i. Form N groups such that cost = N i=1 di is minimized.
Output the minimum cost. Constraints: 1 ≤ N ≤ 8 and 0 ≤ x, y ≤ 1000.
Sample input:
N = 2; Coordinates of the 2N = 4 houses are {1, 1}, {8, 6}, {6, 8}, and {1, 3}.
Sample output:
cost = 4.83.
Can you solve this problem?
If so, how many minutes would you likely require to complete the working code?
Think and try not to flip this page immediately!
1
Some programming competitions are done in a team setting to encourage teamwork as software engineers
usually do not work alone in real life.
2
By hiding the actual test data from the problem statement, competitive programming encourages the
problem solvers to exercise their mental strength to think of all possible corner cases of the problem and
test their programs with those cases. This is typical in real life where software engineers have to test their
software a lot to make sure that the software meets the requirements set by clients.

1
1.1. COMPETITIVE PROGRAMMING 
c Steven & Felix

Figure 1.1: Illustration of UVa 10911 - Forming Quiz Teams

Now ask yourself: Which of the following best describes you? Note that if you are
unclear with the material or the terminology shown in this chapter, you can re-read it
again after going through this book once.
• Uncompetitive programmer A (a.k.a. the blurry one):
Step 1: Reads the problem and becomes confused. (This problem is new for him).
Step 2: Tries to code something: Reading the non-trivial input and output.
Step 3: Realizes that all his attempts are not Accepted (AC):
Greedy (Section 3.4): Repeatedly pairing the two remaining students with the
shortest separating distances gives the Wrong Answer (WA).
Naı̈ve Complete Search: Using recursive backtracking (Section 3.2) and trying
all possible pairings yields Time Limit Exceeded (TLE).
• Uncompetitive programmer B (Give up):
Step 1: Reads the problem and realizes that he has seen this problem before.
But also remembers that he has not learned how to solve this kind of problem...
He is not aware of the Dynamic Programming (DP) solution (Section 3.5)...
Step 2: Skips the problem and reads another problem in the problem set.
• (Still) Uncompetitive programmer C (Slow):
Step 1: Reads the problem and realizes that it is a hard problem: ‘minimum
weight perfect matching on a small general weighted graph’. However,
since the input size is small, this problem is solvable using DP. The DP state is
a bitmask that describes a matching status, and matching unmatched students
i and j will turn on two bits i and j in the bitmask (Section 8.3.1).
Step 2: Codes I/O routine, writes recursive top-down DP, tests, debugs >.<...
Step 3: After 3 hours, his solution obtains AC (passed all the secret test data).
• Competitive programmer D:
Completes all the steps taken by uncompetitive programmer C in ≤ 30 minutes.
• Very competitive programmer E:
A very competitive programmer (e.g. the red ‘target’ coders in TopCoder [32])
would solve this ‘well known’ problem ≤ 15 minutes...

Please note that being well-versed in competitive programming is not the end goal, but
only a means to an end. The true end goal is to produce all-rounder computer scien-
tists/programmers who are much readier to produce better software and to face harder CS
research problems in the future. The founders of the ACM International Collegiate Pro-
gramming Contest (ICPC) [66] have this vision and we, the authors, agree with it. With
this book, we play our little role in preparing the current and the future generations to be
more competitive in dealing with well-known CS problems frequently posed in the recent
ICPCs and the International Olympiad in Informatics (IOI)s.

2
CHAPTER 1. INTRODUCTION 
c Steven & Felix

Exercise 1.1.1: The greedy strategy of the uncompetitive programmer A above actually
works for the sample test case shown in Figure 1.1. Please give a better counter example!
Exercise 1.1.2: Analyze the time complexity of the naı̈ve complete search solution by
uncompetitive programmer A above to understand why it receives the TLE verdict!
Exercise 1.1.3*: Actually, a clever recursive backtracking solution with pruning can still
solve this problem. Solve this problem without using a DP table!

1.2 Tips to be Competitive


If you strive to be like competitive programmers D or E as illustrated above—that is, if
you want to be selected (via provincial/state → national team selections) to participate and
obtain a medal in the IOI [34], or to be one of the team members that represents your
University in the ACM ICPC [66] (nationals → regionals → and up to world finals), or to
do well in other programming contests—then this book is definitely for you!
In the subsequent chapters, you will learn everything from the basic to the intermediate
or even to the advanced3 data structures and algorithms that have frequently appeared in
recent programming contests, compiled from many sources [50, 9, 56, 7, 40, 58, 42, 60, 1,
38, 8, 59, 41, 62, 46] (see Figure 1.4). You will not only learn the concepts behind the data
structures and algorithms, but also how to implement them efficiently and apply them to
appropriate contest problems. On top of that, you will also learn many programming tips
derived from our own experiences that can be helpful in contest situations. We start this
book by giving you several general tips below:

1.2.1 Tip 1: Type Code Faster!


No kidding! Although this tip may not mean much as ICPC and (especially) IOI are not
typing contests, we have seen Rank i and Rank i + 1 ICPC teams separated only by a few
minutes and frustrated IOI contestants who miss out on salvaging important marks by not
being able to code a last-minute brute force solution properly. When you can solve the same
number of problems as your competitor, it will then be down to coding skill (your ability to
produce concise and robust code) and ... typing speed.
Try this typing test at http://www.typingtest.com and follow the instructions there
on how to improve your typing skill. Steven’s is ∼85-95 wpm and Felix’s is ∼55-65 wpm. If
your typing speed is much less than these numbers, please take this tip seriously!
On top of being able to type alphanumeric characters quickly and correctly, you will
also need to familiarize your fingers with the positions of the frequently used programming
language characters: parentheses () or {} or square brackets [] or angle brackets <>, the
semicolon ; and colon :, single quotes ‘’ for characters, double quotes “” for strings, the
ampersand &, the vertical bar or the ‘pipe’ |, the exclamation mark !, etc.
As a little practice, try typing the C++ source code below as fast as possible.

#include <algorithm> // if you have problems with this C++ code,


#include <cmath> // consult your programming text books first...
#include <cstdio>
#include <cstring>
using namespace std;
3
Whether you perceive the material presented in this book to be of intermediate or advanced difficulty
depends on your programming skill prior to reading this book.

3
1.2. TIPS TO BE COMPETITIVE 
c Steven & Felix

/* Forming Quiz Teams, the solution for UVa 10911 above */


// using global variables is a bad software engineering practice,
int N, target; // but it is OK for competitive programming
double dist[20][20], memo[1 << 16]; // 1 << 16 = 2^16, note that max N = 8

double matching(int bitmask) { // DP state = bitmask


// we initialize ‘memo’ with -1 in the main function
if (memo[bitmask] > -0.5) // this state has been computed before
return memo[bitmask]; // simply lookup the memo table
if (bitmask == target) // all students are already matched
return memo[bitmask] = 0; // the cost is 0

double ans = 2000000000.0; // initialize with a large value


int p1, p2;
for (p1 = 0; p1 < 2 * N; p1++)
if (!(bitmask & (1 << p1)))
break; // find the first bit that is off
for (p2 = p1 + 1; p2 < 2 * N; p2++) // then, try to match p1
if (!(bitmask & (1 << p2))) // with another bit p2 that is also off
ans = min(ans, // pick the minimum
dist[p1][p2] + matching(bitmask | (1 << p1) | (1 << p2)));

return memo[bitmask] = ans; // store result in a memo table and return


}

int main() {
int i, j, caseNo = 1, x[20], y[20];
// freopen("10911.txt", "r", stdin); // redirect input file to stdin

while (scanf("%d", &N), N) { // yes, we can do this :)


for (i = 0; i < 2 * N; i++)
scanf("%*s %d %d", &x[i], &y[i]); // ’%*s’ skips names
for (i = 0; i < 2 * N - 1; i++) // build pairwise distance table
for (j = i + 1; j < 2 * N; j++) // have you used ‘hypot’ before?
dist[i][j] = dist[j][i] = hypot(x[i] - x[j], y[i] - y[j]);

// use DP to solve min weighted perfect matching on small general graph


for (i = 0; i < (1 << 16); i++) memo[i] = -1.0; // set -1 to all cells
target = (1 << (2 * N)) - 1;
printf("Case %d: %.2lf\n", caseNo++, matching(0));
} } // return 0;

For your reference, the explanation of this ‘Dynamic Programming with bitmask’ solution
is given in Section 2.2, 3.5, and 8.3.1. Do not be alarmed if you do not understand it yet.

1.2.2 Tip 2: Quickly Identify Problem Types


In ICPCs, the contestants (teams) are given a set of problems (≈ 7-12 problems) of varying
types. From our observation of recent ICPC Asia Regional problem sets, we can categorize
the problems types and their rate of appearance as in Table 1.1.

4
CHAPTER 1. INTRODUCTION 
c Steven & Felix

In IOIs, the contestants are given 6 tasks over 2 days (8 tasks over 2 days in 2009-2010) that
cover items 1-5 and 10, with a much smaller subset of items 6-10 in Table 1.1. For details,
please refer to the 2009 IOI syllabus [20] and the IOI 1989-2008 problem classification [67].
No Category In This Book Frequency
1. Ad Hoc Section 1.4 1-2
2. Complete Search (Iterative/Recursive) Section 3.2 1-2
3. Divide and Conquer Section 3.3 0-1
4. Greedy (usually the original ones) Section 3.4 0-1
5. Dynamic Programming (usually the original ones) Section 3.5 1-3
6. Graph Chapter 4 1-2
7. Mathematics Chapter 5 1-2
8. String Processing Chapter 6 1
9. Computational Geometry Chapter 7 1
10. Some Harder/Rare Problems Chapter 8-9 1-2
Total in Set 8-17 (≈≤ 12)
Table 1.1: Recent ACM ICPC (Asia) Regional Problem Types

The classification in Table 1.1 is adapted from [48] and by no means complete. Some tech-
niques, e.g. ‘sorting’, are not classified here as they are ‘trivial’ and usually used only as a
‘sub-routine’ in a bigger problem. We do not include ‘recursion’ as it is embedded in cate-
gories like recursive backtracking or Dynamic Programming. We also omit ‘data structures’
as the usage of efficient data structure can be considered to be integral for solving harder
problems. Of course, problems sometimes require mixed techniques: A problem can be clas-
sified into more than one type. For example, Floyd Warshall’s algorithm is both a solution
for the All-Pairs Shortest Paths (APSP, Section 4.5) graph problem and a Dynamic Pro-
gramming (DP) algorithm (Section 3.5). Prim’s and Kruskal’s algorithms are both solutions
for the Minimum Spanning Tree (MST, Section 4.3) graph problem and Greedy algorithms
(Section 3.4). In Section 8.4, we will discuss (harder) problems that require more than one
algorithms and/or data structures to be solved.
In the (near) future, these classifications may change. One significant example is Dynamic
Programming. This technique was not known before 1940s, nor frequently used in ICPCs or
IOIs before mid 1990s, but it is considered a definite prerequisite today. As an illustration:
There were ≥ 3 DP problems (out of 11) in the recent ICPC World Finals 2010.
However, the main goal is not just to associate problems with the techniques required to
solve them like in Table 1.1. Once you are familiar with most of the topics in this book, you
should also be able to classify problems into the three types in Table 1.2.

No Category Confidence and Expected Solving Speed


A. I have solved this type before I am sure that I can re-solve it again (and fast)
B. I have seen this type before But that time I know I cannot solve it yet
C. I have not seen this type before See discussion below

Table 1.2: Problem Types (Compact Form)

To be competitive, that is, do well in a programming contest, you must be able to confidently
and frequently classify problems as type A and minimize the number of problems that you
classify into type B. That is, you need to acquire sufficient algorithm knowledge and develop
your programming skills so that you consider many classical problems to be easy. However,
to win a programming contest, you will also need to develop sharp problem solving skills
(e.g. reducing the given problem to a known problem, identifying subtle hints or special

5
1.2. TIPS TO BE COMPETITIVE 
c Steven & Felix

properties in the problem, attacking the problem from a non obvious angle, etc) so that you
(or your team) will be able to derive the required solution to a hard/original type C problem
in IOI or ICPC Regionals/World Finals and do so within the duration of the contest.

UVa Title Problem Type Hint


10360 Rat Attack Complete Search or DP Section 3.2
10341 Solve It Section 3.3
11292 Dragon of Loowater Section 3.4
11450 Wedding Shopping Section 3.5
10911 Forming Quiz Teams DP with bitmask Section 8.3.1
11635 Hotel Booking Section 8.4
11506 Angry Programmer Section 4.6
10243 Fire! Fire!! Fire!!! Section 4.7.1
10717 Mint Section 8.4
11512 GATTACA Section 6.6
10065 Useless Tile Packers Section 7.3.7

Table 1.3: Exercise: Classify These UVa Problems

Exercise 1.2.1: Read the UVa [47] problems shown in Table 1.3 and determine their problem
types. Two of them have been identified for you. Filling this table is easy after mastering
this book—all the techniques required to solve these problems are discussed in this book.

1.2.3 Tip 3: Do Algorithm Analysis


Once you have designed an algorithm to solve a particular problem in a programming contest,
you must then ask this question: Given the maximum input bound (usually given in a good
problem description), can the currently developed algorithm, with its time/space complexity,
pass the time/memory limit given for that particular problem?
Sometimes, there are more than one way to attack a problem. Some approaches may be
incorrect, others not fast enough, and yet others ‘overkill’. A good strategy is to brainstorm
for many possible algorithms and then pick the simplest solution that works (i.e. is fast
enough to pass the time and memory limit and yet still produce the correct answer)4 !
Modern computers are quite fast and can process5 up to ≈ 100M (or 108 ; 1M = 1, 000, 000)
operations in a few seconds. You can use this information to determine if your algorithm will
run in time. For example, if the maximum input size n is 100K (or 105 ; 1K = 1, 000), and
your current algorithm has a time complexity of O(n2 ), common sense (or your calculator)
will inform you that (100K)2 or 1010 is a very large number that indicates that your algo-
rithm will require (on the order of) hundreds of seconds to run. You will thus need to devise
a faster (and also correct) algorithm to solve the problem. Suppose you find one that runs
with a time complexity of O(n log2 n). Now, your calculator will inform you that 105 log2 105
is just 1.7 × 106 and common sense dictates that the algorithm (which should now run in
under a second) will likely be able to pass the time limit.
4
Discussion: It is true that in programming contests, picking the simplest algorithm that works is crucial
for doing well in that programming contest. However, during training sessions, where time constraints are
not an issue, it can be beneficial to spend more time trying to solve a certain problem using the best possible
algorithm. We are better prepared this way. If we encounter a harder version of the problem in the future,
we will have a greater chance of obtaining and implementing the correct solution!
5
Treat this as a rule of thumb. This numbers may vary from machine to machine.

6
CHAPTER 1. INTRODUCTION 
c Steven & Felix

The problem bounds are as important as your algorithm’s time complexity in determining
if your solution is appropriate. Suppose that you can only devise a relatively-simple-to-code
algorithm that runs with a horrendous time complexity of O(n4 ). This may appear to be
an infeasible solution, but if n ≤ 50, then you have actually solved the problem. You can
implement your O(n4 ) algorithm with impunity since 504 is just 6.25M and your algorithm
should still run in around a second.
Note, however, that the order of complexity does not necessarily indicate the actual
number of operations that your algorithm will require. If each iteration involves a large
number of operations (many floating point calculations, or a significant number of constant
sub-loops), or if your implementation has a high ‘constant‘ in its execution (unnecessarily
repeated loops or multiple passes, or even I/O or execution overhead), your code may take
longer to execute than expected. However, this will usually not be the case as the problem
authors should have designed the time limits so that a well-coded algorithm with a suitable
time complexity will achieve an AC verdict.
By analyzing the complexity of your algorithm with the given input bound and the stated
time/memory limit, you can better decide whether you should attempt to implement your
algorithm (which will take up precious time in the ICPCs and IOIs), attempt to improve
your algorithm first, or switch to other problems in the problem set.
As mentioned in the preface of this book, we will not discuss the concept of algorithmic
analysis in details. We assume that you already have this basic skill. There are a multitude
of other reference books (for example, the “Introduction to Algorithms” [7], “Algorithm De-
sign” [38], “Algorithms” [8], etc) that will help you to understand the following prerequisite
concepts/techniques in algorithmic analysis:

• Basic time and space complexity analysis for iterative and recursive algorithms:

– An algorithm with k-nested loops of about n iterations each has O(nk ) complexity.
– If your algorithm is recursive with b recursive calls per level and has L levels, the
algorithm has roughly O(bL) complexity, but this is a only a rough upper bound.
The actual complexity depends on what actions are done per level and whether
pruning is possible.
– A Dynamic Programming algorithm or other iterative routine which processes a
2D n × n matrix in O(k) per cell runs in O(k × n2 ) time. This is explained in
further detail in Section 3.5.

• More advanced analysis techniques:

– Prove the correctness of an algorithm (especially for Greedy algorithms in Section


3.4), to minimize your chance of getting the ‘Wrong Answer’ verdict.
– Perform the amortized analysis (e.g. see Chapter 17 of [7])—although rarely
used in contests—to minimize your chance of getting the ‘Time Limit Exceeded’
verdict, or worse, considering your algorithm to be too slow and skips the problem
when it is in fact fast enough in amortized sense.
– Do output-sensitive analysis to analyze algorithm which (also) depends on output
size and minimize your chance of getting the ‘Time Limit Exceeded’ verdict. For
example, an algorithm to search for a string with length m in a long string with
the help of a Suffix Tree (that is already built) runs in O(m + occ) time. The time
taken for this algorithm to run depends not only on the input size m but also the
output size—the number of occurrences occ (see more details in Section 6.6).

7
1.2. TIPS TO BE COMPETITIVE 
c Steven & Felix

• Familiarity with these bounds:


– 210 = 1, 024 ≈ 103 , 220 = 1, 048, 576 ≈ 106 .
– 32-bit signed integers (int) and 64-bit signed integers (long long) have upper
limits of 231 −1 ≈ 2 ×109 (safe for up to ≈ 9 decimal digits) and 263 −1 ≈ 9 ×1018
(safe for up to ≈ 18 decimal digits) respectively.
– Unsigned integers can be used if only non-negative numbers are required. 32-bit
unsigned integers (unsigned int) and 64-bit unsigned integers (unsigned long
long) have upper limits of 232 − 1 ≈ 4 × 109 and 264 − 1 ≈ 1.8 × 1019 respectively.
– If you need to store integers ≥ 264 , use the Big Integer technique (Section 5.3).
– There are n! permutations and 2n subsets (or combinations) of n elements.
– The best time complexity of a comparison-based sorting algorithm is Ω(n log2 n).
– Usually, O(n log2 n) algorithms are sufficient to solve most contest problems.
– The largest input size for typical programming contest problems must be < 1M.
Beyond that, the time needed to read the input (the Input/Output routine) will
be the bottleneck.
– A typical year 2013 CPU can process 100M = 108 operations in a few seconds.

Many novice programmers would skip this phase and immediately begin implementing the
first (naı̈ve) algorithm that they can think of only to realize that the chosen data structure
or algorithm is not efficient enough (or wrong). Our advice for ICPC contestants6 : Refrain
from coding until you are sure that your algorithm is both correct and fast enough.

n Worst AC Algorithm Comment


≤ [10..11] O(n!), O(n6) e.g. Enumerating permutations (Section 3.2)
≤ [15..18] O(2n × n2 ) e.g. DP TSP (Section 3.5.2)
≤ [18..22] O(2n × n) e.g. DP with bitmask technique (Section 8.3.1)
≤ 100 O(n4 ) e.g. DP with 3 dimensions + O(n) loop, n Ck=4
≤ 400 O(n3 ) e.g. Floyd Warshall’s (Section 4.5)
≤ 2K O(n2 log2 n) e.g. 2-nested loops + a tree-related DS (Section 2.3)
≤ 10K O(n2 ) e.g. Bubble/Selection/Insertion Sort (Section 2.2)
≤ 1M O(n log2 n) e.g. Merge Sort, building Segment Tree (Section 2.3)
≤ 100M O(n), O(log2 n), O(1) Most contest problem has n ≤ 1M (I/O bottleneck)

Table 1.4: Rule of thumb time complexities for the ‘Worst AC Algorithm’ for various single-
test-case input sizes n, assuming that your CPU can compute 100M items in 3s.

To help you understand the growth of several common time complexities, and thus help you
judge how fast is ‘enough’, refer to Table 1.4. Variants of such tables are also found in many
other books on data structures and algorithms. This table is written from a programming
contestant’s perspective. Usually, the input size constraints are given in a (good) problem
description. With the assumption that a typical CPU can execute a hundred million opera-
tions in around 3 seconds (the typical time limit in most UVa [47] problems), we can predict
the ‘worst’ algorithm that can still pass the time limit. Usually, the simplest algorithm has
the poorest time complexity, but if it can pass the time limit, just use it!
6
Unlike ICPC, the IOI tasks can usually be solved (partially or fully) using several possible solutions,
each with different time complexities and subtask scores. To gain valuable points, it may be good to use a
brute force solution to score a few points and to understand the problem better. There will be no significant
time penalty as IOI is not a speed contest. Then, iteratively improve the solution to gain more points.

8
CHAPTER 1. INTRODUCTION 
c Steven & Felix

From Table 1.4, we see the importance of using good algorithms with small orders of growth
as they allow us to solve problems with larger input sizes. But a faster algorithm is usually
non-trivial and sometimes substantially harder to implement. In Section 3.2.3, we discuss a
few tips that may allow the same class of algorithms to be used with larger input sizes. In
subsequent chapters, we also explain efficient algorithms for various computing problems.

Exercise 1.2.2: Please answer the following questions below using your current knowledge
about classic algorithms and their time complexities. After you have finished reading this
book once, it may be beneficial to attempt this exercise again.
1. There are n webpages (1 ≤ n ≤ 10M). Each webpage i has a page rank ri . You want
to pick the top 10 pages with the highest page ranks. Which method is better?
(a) Load all n webpages’ page rank to memory, sort (Section 2.2) them in descending
page rank order, obtaining the top 10.
(b) Use a priority queue data structure (a heap) (Section 2.3).

2. Given an M × N integer matrix Q (1 ≤ M, N ≤ 30), determine if there exists a


sub-matrix of Q of size A × B (1 ≤ A ≤ M, 1 ≤ B ≤ N) where mean(Q) = 7.
(a) Try all possible sub-matrices and check if the mean of each sub-matrix is 7.
This algorithm runs in O(M 3 × N 3 ).
(b) Try all possible sub-matrices, but in O(M 2 × N 2 ) with this technique: .

3. Given a list L with 10K integers, you need to frequently obtain sum(i, j), i.e. the
sum of L[i] + L[i+1] + ...+ L[j]. Which data structure should you use?

(a) Simple Array (Section 2.2).


(b) Simple Array pre-processed with Dynamic Programming (Section 2.2 & 3.5).
(c) Balanced Binary Search Tree (Section 2.3).
(d) Binary Heap (Section 2.3).
(e) Segment Tree (Section 2.4.3).
(f) Binary Indexed (Fenwick) Tree (Section 2.4.4).
(g) Suffix Tree (Section 6.6.2) or its alternative, Suffix Array (Section 6.6.4).

4. Given a set S of N points randomly scattered on a 2D plane (2 ≤ N ≤ 1000), find


two points ∈ S that have the greatest separating Euclidean distance. Is an O(N 2 )
complete search algorithm that tries all possible pairs feasible?

(a) Yes, such complete search is possible.


(b) No, we must find another way. We must use: .

5. You have to compute the shortest path between two vertices on a weighted Directed
Acyclic Graph (DAG) with |V |, |E| ≤ 100K. Which algorithm(s) can be used in a
typical programming contest (that is, with a time limit of approximately 3 seconds)?

(a) Dynamic Programming (Section 3.5, 4.2.5, & 4.7.1).


(b) Breadth First Search (Section 4.2.2 & 4.4.2).
(c) Dijkstra’s (Section 4.4.3).

9
1.2. TIPS TO BE COMPETITIVE 
c Steven & Felix

(d) Bellman Ford’s (Section 4.4.4).


(e) Floyd Warshall’s (Section 4.5).

6. Which algorithm produces a list of the first 10K prime numbers with the best time
complexity? (Section 5.5.1)

(a) Sieve of Eratosthenes (Section 5.5.1).


(b) For each number i ∈ [1..10K], test if isPrime(i) is true (Section 5.5.1).

7. You want to test if the factorial of n, i.e. n! is divisible by an integer m. 1 ≤ n ≤ 10000.


What should you do?

(a) Test if n! % m == 0.
(b) The naı̈ve approach above will not work, use: (Section 5.5.1).

8. Question 4, but with a larger set of points: N ≤ 1M and one additional constraint:
The points are randomly scattered on a 2D plane.

(a) The complete search mentioned in question 3 can still be used.


(b) The naı̈ve approach above will not work, use: (Section 7.3.7).

9. You want to enumerate all occurrences of a substring P (of length m) in a (long) string
T (of length n), if any. Both n and m have a maximum of 1M characters.
(a) Use the following C++ code snippet:
for (int i = 0; i < n; i++) {
bool found = true;
for (int j = 0; j < m && found; j++)
if (i + j >= n || P[j] != T[i + j]) found = false;
if (found) printf("P is found at index %d in T\n", i);
}

(b) The naı̈ve approach above will not work, use: (Section 6.4 or 6.6).

1.2.4 Tip 4: Master Programming Languages


There are several programming languages supported in ICPC7 , including C/C++ and Java.
Which programming languages should one aim to master?
Our experience gives us this answer: We prefer C++ with its built-in Standard Template
Library (STL) but we still need to master Java. Even though it is slower, Java has powerful
built-in libraries and APIs such as BigInteger/BigDecimal, GregorianCalendar, Regex, etc.
Java programs are easier to debug with the virtual machine’s ability to provide a stack trace
7
Personal opinion: According to the latest IOI 2012 competition rules, Java is currently still not supported
in IOI. The programming languages allowed in IOI are C, C++, and Pascal. On the other hand, the ICPC
World Finals (and thus most Regionals) allows C, C++ and Java to be used in the contest. Therefore, it is
seems that the ‘best’ language to master is C++ as it is supported in both competitions and it has strong
STL support. If IOI contestants choose to master C++, they will have the benefit of being able to use the
same language (with an increased level of mastery) for ACM ICPC in their University level pursuits.

10
CHAPTER 1. INTRODUCTION 
c Steven & Felix

when it crashes (as opposed to core dumps or segmentation faults in C/C++). On the
other hand, C/C++ has its own merits as well. Depending on the problem at hand, either
language may be the better choice for implementing a solution in the shortest time.
Suppose that a problem requires you to compute 25! (the factorial of 25). The answer is
very large: 15,511,210,043,330,985,984,000,000. This far exceeds the largest built-in primitive
integer data type (unsigned long long: 264 − 1). As there is no built-in arbitrary-precision
arithmetic library in C/C++ as of yet, we would have needed to implement one from scratch.
The Java code, however, is exceedingly simple (more details in Section 5.3). In this case,
using Java definitely makes for shorter coding time.

import java.util.Scanner;
import java.math.BigInteger;

class Main { // standard Java class name in UVa OJ


public static void main(String[] args) {
BigInteger fac = BigInteger.ONE;
for (int i = 2; i <= 25; i++)
fac = fac.multiply(BigInteger.valueOf(i)); // it is in the library!
System.out.println(fac);
} }

Mastering and understanding the full capability of your favourite programming language is
also important. Take this problem with a non-standard input format: the first line of input
is an integer N. This is followed by N lines, each starting with the character ‘0’, followed
by a dot ‘.’, then followed by an unknown number of digits (up to 100 digits), and finally
terminated with three dots ‘...’.

3
0.1227...
0.517611738...
0.7341231223444344389923899277...

One possible solution is as follows:

#include <cstdio>
using namespace std;

int N; // using global variables in contests can be a good strategy


char x[110]; // make it a habit to set array size a bit larger than needed

int main() {
scanf("%d\n", &N);
while (N--) { // we simply loop from N, N-1, N-2, ..., 0
scanf("0.%[0-9]...\n", &x); // ‘&’ is optional when x is a char array
// note: if you are surprised with the trick above,
// please check scanf details in www.cppreference.com
printf("the digits are 0.%s\n", x);
} } // return 0;

Source code: ch1 01 factorial.java; ch1 02 scanf.cpp

11
1.2. TIPS TO BE COMPETITIVE 
c Steven & Felix

Not many C/C++ programmers are aware of partial regex capabilities built into the C
standard I/O library. Although scanf/printf are C-style I/O routines, they can still be
used in C++ code. Many C++ programmers ‘force’ themselves to use cin/cout all the time
even though it is sometimes not as flexible as scanf/printf and is also far slower.
In programming contests, especially ICPCs, coding time should not be the primary
bottleneck. Once you figure out the ‘worst AC algorithm’ that will pass the given time limit,
you are expected to be able to translate it into a bug-free code and fast!
Now, try some of the exercises below! If you need more than 10 lines of code to solve any
of them, you should revisit and update your knowledge of your programming language(s)!
A mastery of the programming languages you use and their built-in routines is extremely
important and will help you a lot in programming contests.

Exercise 1.2.3: Produce working code that is as concise as possible for the following tasks:
1. Using Java, read in a double
(e.g. 1.4732, 15.324547327, etc.)
echo it, but with a minimum field width of 7 and 3 digits after the decimal point
(e.g. ss1.473 (where ‘s’ denotes a space), s15.325, etc.)
2. Given an integer n (n ≤ 15), print π to n digits after the decimal point (rounded).
(e.g. for n = 2, print 3.14; for n = 4, print 3.1416; for n = 5, prints 3.14159.)
3. Given a date, determine the day of the week (Monday, . . . , Sunday) on that day.
(e.g. 9 August 2010—the launch date of the first edition of this book—is a Monday.)
4. Given n random integers, print the distinct (unique) integers in sorted order.
5. Given the distinct and valid birthdates of n people as triples (DD, MM, YYYY), order
them first by ascending birth months (MM), then by ascending birth dates (DD), and
finally by ascending age.
6. Given a list of sorted integers L of size up to 1M items, determine whether a value v
exists in L with no more than 20 comparisons (more details in Section 2.2).
7. Generate all possible permutations of {‘A’, ‘B’, ‘C’, . . . , ‘J’}, the first N = 10 letters
in the alphabet (see Section 3.2.1).
8. Generate all possible subsets of {0, 1, 2, . . . , N-1}, for N = 20 (see Section 3.2.1).
9. Given a string that represents a base X number, convert it to an equivalent string in
base Y, 2 ≤ X, Y ≤ 36. For example: “FF” in base X = 16 (hexadecimal) is “255” in
base Y1 = 10 (decimal) and “11111111” in base Y2 = 2 (binary). See Section 5.3.2.
10. Let’s define a ‘special word’ as a lowercase alphabet followed by two consecutive digits.
Given a string, replace all ‘special words’ of length 3 with 3 stars “***”, e.g.
S = “line: a70 and z72 will be replaced, aa24 and a872 will not”
should be transformed into:
S = “line: *** and *** will be replaced, aa24 and a872 will not”.
11. Given a valid mathematical expression involving ‘+’, ‘-’, ‘*’, ‘/’, ‘(’, and ‘)’ in a single
line, evaluate that expression. (e.g. a rather complicated but valid expression 3 + (8 -
7.5) * 10 / 5 - (2 + 5 * 7) should produce -33.0 when evaluated with standard
operator precedence.)

12
CHAPTER 1. INTRODUCTION 
c Steven & Felix

1.2.5 Tip 5: Master the Art of Testing Code


You thought you nailed a particular problem. You identified its problem type, designed
the algorithm for it, verified that the algorithm (with the data structures it uses) will run
in time (and within memory limits) by considering the time (and space) complexity, and
implemented the algorithm, but your solution is still not Accepted (AC).
Depending on the programming contest, you may or may not get credit for solving the
problem partially. In ICPC, you will only get points for a particular problem if your team’s
code solves all the secret test cases for that problem. Other verdicts such as Presentation
Error (PE), Wrong Answer (WA), Time Limit Exceeded (TLE), Memory Limit Exceeded
(MLE), Run Time Error (RTE), etc. do not increase your team’s points. In current IOI
(2010-2012), the subtask scoring system is used. Test cases are grouped into subtasks, usually
simpler variants of the original task with smaller input bounds. You will only be credited
for solving a subtask if your code solves all test cases in it. You are given tokens that you
can use (sparingly) throughout the contest to view the judge’s evaluation of your code.
In either case, you will need to be able to design good, comprehensive and tricky test
cases. The sample input-output given in the problem description is by nature trivial and
therefore usually not a good means for determining the correctness of your code.
Rather than wasting submissions (and thus accumulating time or score penalties) in
ICPC or tokens in IOI, you may want to design tricky test cases for testing your code on
your own machine8 . Ensure that your code is able to solve them correctly (otherwise, there
is no point submitting your solution since it is likely to be incorrect—unless you want to test
the test data bounds).
Some coaches encourage their students to compete with each other by designing test
cases. If student A’s test cases can break student B’s code, then A will get bonus points.
You may want to try this in your team training :).
Here are some guidelines for designing good test cases from our experience.
These are typically the steps that have been taken by problem authors.
1. Your test cases should include the sample test cases since the sample output is guaran-
teed to be correct. Use ‘fc’ in Windows or ‘diff’ in UNIX to check your code’s output
(when given the sample input) against the sample output. Avoid manual comparison
as humans are prone to error and are not good at performing such tasks, especially
for problems with strict output formats (e.g. blank line between test cases versus after
every test cases). To do this, copy and paste the sample input and sample output
from the problem description, then save them to files (named as ‘input’ and ‘output’
or anything else that is sensible). Then, after compiling your program (let’s assume
the executable’s name is the ‘g++’ default ‘a.out’), execute it with an I/O redirec-
tion: ‘./a.out < input > myoutput’. Finally, execute ‘diff myoutput output’ to
highlight any (potentially subtle) differences, if any exist.
2. For problems with multiple test cases in a single run (see Section 1.3.2), you should
include two identical sample test cases consecutively in the same run. Both must
output the same known correct answers. This helps to determine if you have forgotten
to initialize any variables—if the first instance produces the correct answer but the
second one does not, it is likely that you have not reset your variables.
3. Your test cases should include tricky corner cases. Think like the problem author and
try to come up with the worst possible input for your algorithm by identifying cases
8
Programming contest environments differ from one contest to another. This can disadvantage contestants
who rely too much on fancy Integrated Development Environment (IDE) (e.g. Visual Studio, Eclipse, etc)
for debugging. It may be a good idea to practice coding with just a text editor and a compiler!

13
1.2. TIPS TO BE COMPETITIVE 
c Steven & Felix

that are ‘hidden’ or implied within the problem description. These cases are usually
included in the judge’s secret test cases but not in the sample input and output. Corner
cases typically occur at extreme values such as N = 0, N = 1, negative values, large
final (and/or intermediate) values that does not fit 32-bit signed integer, etc.
4. Your test cases should include large cases. Increase the input size incrementally up to
the maximum input bounds stated in the problem description. Use large test cases with
trivial structures that are easy to verify with manual computation and large random
test cases to test if your code terminates in time and still produces reasonable output
(since the correctness would be difficult to verify here). Sometimes your program may
work for small test cases, but produces wrong answer, crashes, or exceeds the time
limit when the input size increases. If that happens, check for overflows, out of bound
errors, or improve your algorithm.
5. Though this is rare in modern programming contests, do not assume that the input
will always be nicely formatted if the problem description does not explicitly state it
(especially for a badly written problem). Try inserting additional whitespace (spaces,
tabs) in the input and test if your code is still able to obtain the values correctly
without crashing.

However, after carefully following all these steps, you may still get non-AC verdicts. In
ICPC, you (and your team) can actually consider the judge’s verdict and the leader board
(usually available for the first four hours of the contest) in determining your next course of
action. In IOI 2010-2012, contestants have a limited number of tokens to use for checking
the correctness of their submitted code against the secret test cases. With more experience
in such contests, you will be able to make better judgments and choices.

Exercise 1.2.4: Situational awareness


(mostly applicable in the ICPC setting—this is not as relevant in IOI).

1. You receive a WA verdict for a very easy problem. What should you do?

(a) Abandon this problem for another.


(b) Improve the performance of your solution (code optimizations/better algorithm).
(c) Create tricky test cases to find the bug.
(d) (In team contests): Ask your team mate to re-do the problem.

2. You receive a TLE verdict for your O(N 3 ) solution.


However, the maximum N is just 100. What should you do?

(a) Abandon this problem for another.


(b) Improve the performance of your solution (code optimizations/better algorithm).
(c) Create tricky test cases to find the bug.

3. Follow up to Question 2: What if the maximum N is 100.000?


4. Another follow up to Question 2: What if the maximum N is 1.000, the output only
depends on the size of input N, and you still have four hours of competition time left?
5. You receive an RTE verdict. Your code (seems to) execute perfectly on your machine.
What should you do?

14
CHAPTER 1. INTRODUCTION 
c Steven & Felix

6. Thirty minutes into the contest, you take a glance at the leader board. There are many
other teams that have solved a problem X that your team has not attempted. What
should you do?
7. Midway through the contest, you take a glance at the leader board. The leading team
(assume that it is not your team) has just solved problem Y . What should you do?
8. Your team has spent two hours on a nasty problem. You have submitted several im-
plementations by different team members. All submissions have been judged incorrect.
You have no idea what’s wrong. What should you do?
9. There is one hour to go before the end of the contest. You have 1 WA code and 1 fresh
idea for another problem. What should you (or your team) do?

(a) Abandon the problem with the WA code, switch to the other problem in an
attempt to solve one more problem.
(b) Insist that you have to debug the WA code. There is not enough time to start
working on a new problem.
(c) (In ICPC): Print the WA code. Ask two other team members to scrutinize it while
you switch to that other problem in an attempt to solve two more problems.

1.2.6 Tip 6: Practice and More Practice


Competitive programmers, like real athletes, must train regularly and keep ‘programming-
fit’. Thus in our second last tip, we provide a list of several websites with resources that
can help improve your problem solving skill. We believe that success comes as a result of a
continuous effort to better yourself.
The University of Valladolid (UVa, from Spain) Online Judge [47] contains past ACM
contest problems (Locals, Regionals, and up to World Finals) plus problems from other
sources, including various problems from contests hosted by UVa. You can solve these
problems and submit your solutions to the Online Judge. The correctness of your program
will be reported as soon as possible. Try solving the problems mentioned in this book and
you might see your name on the top-500 authors rank list someday :-).
As of 24 May 2013, one needs to solve ≥ 542 problems to be in the top-500. Steven is
ranked 27 (for solving 1674 problems) while Felix is ranked 37 (for solving 1487 problems)
out of ≈ 149008 UVa users (and a total of ≈ 4097 problems).
UVa’s ‘sister’ online judge is the ACM ICPC Live Archive [33] that contains almost all
recent ACM ICPC Regionals and World Final problem sets since year 2000. Train here if
you want to do well in future ICPCs. Note that in October 2011, about hundreds of Live
Archive problems (including the ones listed in the second edition of this book) are mirrored
in the UVa Online Judge.

Figure 1.2: Left: University of Valladolid Online Judge; Right: ACM ICPC Live Archive.

15
1.3. GETTING STARTED: THE EASY PROBLEMS 
c Steven & Felix

The USA Computing Olympiad has a very useful training website [48] with online contests
to help you learn programming and problem solving skills. This is geared for IOI participants
more than for ICPC participants. Go straight to their website and train.
The Sphere Online Judge [61] is another online judge where qualified users can add their
problems. This online judge is quite popular in countries like Poland, Brazil, and Vietnam.
We have used this SPOJ to publish some of our self-authored problems.

Figure 1.3: Left: USACO Training Gateway; Right: Sphere Online Judge

TopCoder arranges frequent ‘Single Round Match’ (SRM) [32] that consists of a few problems
to be solved in 1-2 hours. After the contest, you are given the chance to ‘challenge’ other
contestants code by supplying tricky test cases. This online judge uses a rating system (red,
yellow, blue, etc coders) to reward contestants who are really good at problem solving with a
higher rating as opposed to more diligent contestants who happen to solve a higher number
of easier problems.

1.2.7 Tip 7: Team Work (for ICPC)


This last tip is not something that is easy to teach, but here are some ideas that may be
worth trying for improving your team’s performance:

• Practice coding on blank paper. (This is useful when your teammate is using the
computer. When it is your turn to use the computer, you can then just type the code
as fast as possible rather than spending time thinking in front of the computer.)

• The ‘submit and print’ strategy: If your code gets an AC verdict, ignore the printout.
If it still is not AC, debug your code using that printout (and let your teammate uses
the computer for other problem). Beware: Debugging without the computer is not an
easy skill to master.

• If your teammate is currently coding his algorithm, prepare challenges for his code by
preparing hard corner-case test data (hopefully his code passes all those).

• The X-factor: Befriend your teammates outside of training sessions and contests.

1.3 Getting Started: The Easy Problems


Note: You can skip this section if you are a veteran participant of programming contests.
This section is meant for readers who are new with competitive programming.

1.3.1 Anatomy of a Programming Contest Problem


A programming contest problem usually contains the following components:

• Background story/problem description. Usually, the easier problems are writ-


ten to deceive contestants and made to appear difficult, for example by adding ‘ex-
tra information’ to create a diversion. Contestants should be able to filter out these

16
CHAPTER 1. INTRODUCTION 
c Steven & Felix

unimportant details and focus on the essential ones. For example, the entire opening
paragraphs except the last sentence in UVa 579 - ClockHands are about the history of
the clock and is completely unrelated to the actual problem. However, harder problems
are usually written as succinctly as possible—they are already difficult enough without
additional embellishment.

• Input and Output description. In this section, you will be given details on how
the input is formatted and on how you should format your output. This part is usually
written in a formal manner. A good problem should have clear input constraints as the
same problem might be solvable with different algorithms for different input constraints
(see Table 1.4).

• Sample Input and Sample Output. Problem authors usually only provide trivial
test cases to contestants. The sample input/output is intended for contestants to check
their basic understanding of the problem and to verify if their code can parse the given
input using the given input format and produce the correct output using the given
output format. Do not submit your code to the judge if it does not even pass the given
sample input/output. See Section 1.2.5 about testing your code before submission.

• Hints or Footnotes. In some cases, the problem authors may drop hints or add
footnotes to further describe the problem.

1.3.2 Typical Input/Output Routines


Multiple Test Cases
In a programming contest problem, the correctness of your code is usually determined by
running your code against several test cases. Rather than using many individual test case
files, modern programming contest problems usually use one test case file with multiple test
cases included. In this section, we use a very simple problem as an example of a multiple-
test-cases problem: Given two integers in one line, output their sum in one line. We will
illustrate three possible input/output formats:

• The number of test cases is given in the first line of the input.

• The multiple test cases are terminated by special values (usually zeroes).

• The multiple test cases are terminated by the EOF (end-of-file) signal.

C/C++ Source Code | Sample Input | Sample Output


---------------------------------------------------------------------------
int TC, a, b; | 3 | 3
scanf("%d", &TC); // number of test cases | 1 2 | 12
while (TC--) { // shortcut to repeat until 0 | 5 7 | 9
scanf("%d %d", &a, &b); // compute answer | 6 3 |--------------
printf("%d\n", a + b); // on the fly |--------------|
} | |
---------------------------------------------------------------------------
int a, b; | 1 2 | 3
// stop when both integers are 0 | 5 7 | 12
while (scanf("%d %d", &a, &b), (a || b)) | 6 3 | 9
printf("%d\n", a + b); | 0 0 |--------------

17
1.3. GETTING STARTED: THE EASY PROBLEMS 
c Steven & Felix

---------------------------------------------------------------------------
int a, b; | 1 2 | 3
// scanf returns the number of items read | 5 7 | 12
while (scanf("%d %d", &a, &b) == 2) | 6 3 | 9
// or you can check for EOF, i.e. |--------------|--------------
// while (scanf("%d %d", &a, &b) != EOF) | |
printf("%d\n", a + b); | |

Case Numbers and Blank Lines


Some problems with multiple test cases require the output of each test case to be numbered
sequentially. Some also require a blank line after each test case. Let’s modify the simple
problem above to include the case number in the output (starting from one) with this output
format: “Case [NUMBER]: [ANSWER]” followed by a blank line for each test case. Assuming
that the input is terminated by the EOF signal, we can use the following code:

C/C++ Source Code | Sample Input | Sample Output


---------------------------------------------------------------------------
int a, b, c = 1; | 1 2 | Case 1: 3
while (scanf("%d %d", &a, &b) != EOF) | 5 7 |
// notice the two ‘\n’ | 6 3 | Case 2: 12
printf("Case %d: %d\n\n", c++, a + b); |--------------|
| | Case 3: 9
| |
| |--------------

Some other problems require us to output blank lines only between test cases. If we use the
approach above, we will end up with an extra new line at the end of our output, producing
unnecessary ‘Presentation Error’ (PE) verdict. We should use the following code instead:

C/C++ Source Code | Sample Input | Sample Output


---------------------------------------------------------------------------
int a, b, c = 1; | 1 2 | Case 1: 3
while (scanf("%d %d", &a, &b) != EOF) { | 5 7 |
if (c > 1) printf("\n"); // 2nd/more cases | 6 3 | Case 2: 12
printf("Case %d: %d\n", c++, a + b); |--------------|
} | | Case 3: 9
| |--------------

Variable Number of Inputs


Let’s change the simple problem above slightly. For each test case (each input line), we are
now given an integer k (k ≥ 1), followed by k integers. Our task is now to output the sum
of these k integers. Assuming that the input is terminated by the EOF signal and we do not
require case numbering, we can use the following code:

C/C++ Source Code | Sample Input | Sample Output


---------------------------------------------------------------------------

18
CHAPTER 1. INTRODUCTION 
c Steven & Felix

int k, ans, v; | 1 1 | 1
while (scanf("%d", &k) != EOF) { | 2 3 4 | 7
ans = 0; | 3 8 1 1 | 10
while (k--) { scanf("%d", &v); ans += v; } | 4 7 2 9 3 | 21
printf("%d\n", ans); | 5 1 1 1 1 1 | 5
} |--------------|--------------

Exercise 1.3.1*: What if the problem author decides to make the input a little more
problematic? Instead of an integer k at the beginning of each test case, you are now required
to sum all integers in each test case (each line). Hint: See Section 6.2.
Exercise 1.3.2*: Rewrite all C/C++ source code in this Section 1.3.2 in Java!

1.3.3 Time to Start the Journey


There is no better way to begin your journey in competitive programming than to solve a
few programming problems. To help you pick problems to start with among the ≈ 4097
problems in UVa online judge [47], we have listed some of the easiest Ad Hoc problems
below. More details about Ad Hoc problems will be presented in the next Section 1.4.

• Super Easy
You should get these problems AC9 in under 7 minutes10 each! If you are new to com-
petitive programming, we strongly recommend that you start your journey by solving
some problems from this category after completing the previous Section 1.3.2. Note:
Since each category contains numerous problems for you to try, we have highlighted a
maximum of three (3) must try * problems in each category. These are the problems
that, we think, are more interesting or are of higher quality.

• Easy
We have broken up the ‘Easy’ category into two smaller ones. The problems in this
category are still easy, but just ‘a bit’ harder than the ‘Super Easy’ ones.

• Medium: One Notch Above Easy


Here, we list some other Ad Hoc problems that may be slightly trickier (or longer)
than those in the ‘Easy’ category.

• Super Easy Problems in the UVa Online Judge (solvable in under 7 minutes)
1. UVa 00272 - TEX Quotes (replace all double quotes to TEX() style quotes)
2. UVa 01124 - Celebrity Jeopardy (LA 2681, just echo/re-print the input again)
3. UVa 10550 - Combination Lock (simple, do as asked)
4. UVa 11044 - Searching for Nessy (one liner code/formula exists)
5. UVa 11172 - Relational Operators * (ad hoc, very easy, one liner)
6. UVa 11364 - Parking (linear scan to get l & r, answer = 2 ∗ (r − l))
7. UVa 11498 - Division of Nlogonia * (just use if-else statements)
9
Do not feel bad if you are unable to do so. There can be many reasons why a code may not get AC.
10
Seven minutes is just a rough estimate. Some of these problems can be solved with one-liners.

19
1.3. GETTING STARTED: THE EASY PROBLEMS 
c Steven & Felix

8. UVa 11547 - Automatic Answer (a one liner O(1) solution exists)


9. UVa 11727 - Cost Cutting * (sort the 3 numbers and get the median)
10. UVa 12250 - Language Detection (LA 4995, KualaLumpur10, if-else check)
11. UVa 12279 - Emoogle Balance (simple linear scan)
12. UVa 12289 - One-Two-Three (just use if-else statements)
13. UVa 12372 - Packing for Holiday (just check if all L, W, H ≤ 20)
14. UVa 12403 - Save Setu (straightforward)
15. UVa 12577 - Hajj-e-Akbar (straightforward)
• Easy (just ‘a bit’ harder than the ‘Super Easy’ ones)
1. UVa 00621 - Secret Research (case analysis for only 4 possible outputs)
2. UVa 10114 - Loansome Car Buyer * (just simulate the process)
3. UVa 10300 - Ecological Premium (ignore the number of animals)
4. UVa 10963 - The Swallowing Ground (for two blocks to be mergable, the
gaps between their columns must be the same)
5. UVa 11332 - Summing Digits (simple recursions)
6. UVa 11559 - Event Planning * (one linear pass)
7. UVa 11679 - Sub-prime (check if after simulation all banks have ≥ 0 reserve)
8. UVa 11764 - Jumping Mario (one linear scan to count high+low jumps)
9. UVa 11799 - Horror Dash * (one linear scan to find the max value)
10. UVa 11942 - Lumberjack Sequencing (check if input is sorted asc/descending)
11. UVa 12015 - Google is Feeling Lucky (traverse the list twice)
12. UVa 12157 - Tariff Plan (LA 4405, KualaLumpur08, compute and compare)
13. UVa 12468 - Zapping (easy; there are only 4 possibilities)
14. UVa 12503 - Robot Instructions (easy simulation)
15. UVa 12554 - A Special ... Song (simulation)
16. IOI 2010 - Cluedo (use 3 pointers)
17. IOI 2010 - Memory (use 2 linear pass)
• Medium: One Notch Above Easy (may take 15-30 minutes, but not too hard)
1. UVa 00119 - Greedy Gift Givers (simulate give and receive process)
2. UVa 00573 - The Snail * (simulation, beware of boundary cases!)
3. UVa 00661 - Blowing Fuses (simulation)
4. UVa 10141 - Request for Proposal * (solvable with one linear scan)
5. UVa 10324 - Zeros and Ones (simplify using 1D array: change counter)
6. UVa 10424 - Love Calculator (just do as asked)
7. UVa 10919 - Prerequisites? (process the requirements as the input is read)
8. UVa 11507 - Bender B. Rodriguez ... * (simulation, if-else)
9. UVa 11586 - Train Tracks (TLE if brute force, find the pattern)
10. UVa 11661 - Burger Time? (linear scan)
11. UVa 11683 - Laser Sculpture (one linear pass is enough)
12. UVa 11687 - Digits (simulation; straightforward)
13. UVa 11956 - Brain**** (simulation; ignore ‘.’)
14. UVa 12478 - Hardest Problem ... (try one of the eight names)
15. IOI 2009 - Garage (simulation)
16. IOI 2009 - POI (sort)

20
CHAPTER 1. INTRODUCTION 
c Steven & Felix

1.4 The Ad Hoc Problems


We will terminate this chapter by discussing the first proper problem type in the ICPCs
and IOIs: The Ad Hoc problems. According to USACO [48], the Ad Hoc problems are
problems that ‘cannot be classified anywhere else’ since each problem description and its
corresponding solution are ‘unique’. Many Ad Hoc problems are easy (as shown in Section
1.3), but this does not apply to all Ad Hoc problems.
Ad Hoc problems frequently appear in programming contests. In ICPC, ≈ 1-2 problems
out of every ≈ 10 problems are Ad Hoc problems. If the Ad Hoc problem is easy, it will
usually be the first problem solved by the teams in a programming contest. However, there
were cases where solutions to the Ad Hoc problems were too complicated to implement,
causing some teams to strategically defer them to the last hour. In an ICPC regional contest
with about 60 teams, your team would rank in the lower half (rank 30-60) if you can only
solve Ad Hoc problems.
In IOI 2009 and 2010, there has been 1 easy task per competition day11 , usually an (Easy)
Ad Hoc task. If you are an IOI contestant, you will definitely not win any medals for just
solving the 2 easy Ad Hoc tasks over the 2 competition days. However, the faster you can
clear these 2 easy tasks, the more time that you will have to work on the other 2 × 3 = 6
challenging tasks.
We have listed many Ad Hoc problems that we have solved in the UVa Online Judge
[47] in the several categories below. We believe that you can solve most of these problems
without using the advanced data structures or algorithms that will be discussed in the later
chapters. Many of these Ad Hoc problems are ‘simple’ but some of them maybe ‘tricky’.
Try to solve a few problems from each category before reading the next chapter.
Note: A small number of problems, although listed as part of Chapter 1, may require
knowledge from subsequent chapters, e.g. knowledge of linear data structures (arrays) in
Section 2.2, knowledge of backtracking in Section 3.2, etc. You can revisit these harder Ad
Hoc problems after you have understood the required concepts.

The categories:

• Game (Card)
There are lots of Ad Hoc problems involving popular games. Many are related to card
games. You will usually need to parse the input strings (see Section 6.3) as playing
cards have both suits (D/Diamond/♦, C/Club/♣, H/Heart/♥, and S/Spades/♠) and
ranks (usually: 2 < 3 < . . . < 9 < T/Ten < J/Jack < Q/Queen < K/King < A/Ace12 ).
It may be a good idea to map these troublesome strings to integer indices. For example,
one possible mapping is to map D2 → 0, D3 → 1, . . . , DA → 12, C2 → 13, C3 → 14,
. . . , SA → 51. Then, you can work with the integer indices instead.

• Game (Chess)
Chess is another popular game that sometimes appears in programming contest prob-
lems. Some of these problems are Ad Hoc and listed in this section. Some of them are
combinatorial with tasks like counting how many ways there are to place 8-queens in
8 × 8 chess board. These are listed in Chapter 3.

• Game (Others), easier and harder (or more tedious)


Other than card and chess games, many other popular games have made their way into
programming contests: Tic Tac Toe, Rock-Paper-Scissors, Snakes/Ladders, BINGO,
11
This was no longer true in IOI 2011-2012 as the easier scores are inside subtask 1 of each task.
12
In some other arrangements, A/Ace < 2.

21
1.4. THE AD HOC PROBLEMS 
c Steven & Felix

Bowling, etc. Knowing the details of these games may be helpful, but most of the
game rules are given in the problem description to avoid disadvantaging contestants
who are unfamiliar with the games.
• Problems related to Palindromes
These are also classic problems. A palindrome is a word (or a sequence) that can
be read the same way in either direction. The most common strategy to check if a
word is palindromic is to loop from the first character to the middle one and check
if the characters match in the corresponding position from the back. For example,
‘ABCDCBA’ is a palindrome. For some harder palindrome-related problems, you
may want to check Section 6.5 for Dynamic Programming solutions.
• Problems related to Anagrams
This is yet another class of classic problems. An anagram is a word (or phrase) whose
letters can be rearranged to obtain another word (or phrase). The common strategy
to check if two words are anagrams is to sort the letters of the words and compare
the results. For example, take wordA = ‘cab’, wordB = ‘bca’. After sorting, wordA
= ‘abc’ and wordB = ‘abc’ too, so they are anagrams. See Section 2.2 for various
sorting techniques.
• Interesting Real Life Problems, easier and harder (or more tedious)
This is one of the most interesting problem categories in the UVa Online Judge. We
believe that real life problems like these are interesting to those who are new to Com-
puter Science. The fact that we write programs to solve real life problems can be
an additional motivational boost. Who knows, you might stand to gain new (and
interesting) information from the problem description!
• Ad Hoc problems involving Time
These problems utilize time concepts such as dates, times, and calendars. These are
also real life problems. As mentioned earlier, these problems can be a little more
interesting to solve. Some of these problems will be far easier to solve if you have
mastered the Java GregorianCalendar class as it has many library functions that deal
with time.
• ‘Time Waster’ problems
These are Ad Hoc problems that are written specifically to make the required solution
long and tedious. These problems, if they do appear in a programming contest, would
determine the team with the most efficient coder—someone who can implement com-
plicated but still accurate solutions under time constraints. Coaches should consider
adding such problems in their training programmes.
• Ad Hoc problems in other chapters
There are many other Ad Hoc problems which we have shifted to other chapters since
they required knowledge above basic programming skills.

– Ad Hoc problems involving the usage of basic linear data structures (especially
arrays) are listed in Section 2.2.
– Ad Hoc problems involving mathematical computation are listed in Section 5.2.
– Ad Hoc problems involving string processing are listed in Section 6.3.
– Ad Hoc problems involving basic geometry are listed in Section 7.2.
– Ad Hoc problems listed in Chapter 9.

22
CHAPTER 1. INTRODUCTION 
c Steven & Felix

Tips: After solving a number of programming problems, you begin to realize a pat-
tern in your solutions. Certain idioms are used frequently enough in competitive pro-
gramming implementation for shortcuts to be useful. From a C/C++ perspective,
these idioms might include: Libraries to be included (cstdio, cmath, cstring, etc),
data type shortcuts (ii, vii, vi, etc), basic I/O routines (freopen, multiple input for-
mat, etc), loop macros (e.g. #define REP(i, a, b) for (int i = int(a); i <=
int(b); i++), etc), and a few others. A competitive programmer using C/C++ can
store these in a header file like ‘competitive.h’. With such a header, the solution to
every problem begins with a simple #include<competitive.h>. However, this tips
should not be used beyond competitive programming, especially in software industry.

Programming Exercises related to Ad Hoc problems:

• Game (Card)
1. UVa 00162 - Beggar My Neighbour (card game simulation; straightforward)
2. UVa 00462 - Bridge Hand Evaluator * (simulation; card)
3. UVa 00555 - Bridge Hands (card game)
4. UVa 10205 - Stack ’em Up (card game)
5. UVa 10315 - Poker Hands (tedious problem)
6. UVa 10646 - What is the Card? * (shuffle cards with some rule and
then get certain card)
7. UVa 11225 - Tarot scores (another card game)
8. UVa 11678 - Card’s Exchange (actually just an array manipulation problem)
9. UVa 12247 - Jollo * (interesting card game; simple, but requires good
logic to get all test cases correct)
• Game (Chess)
1. UVa 00255 - Correct Move (check the validity of chess moves)
2. UVa 00278 - Chess * (ad hoc, chess, closed form formula exists)
3. UVa 00696 - How Many Knights * (ad hoc, chess)
4. UVa 10196 - Check The Check (ad hoc chess game, tedious)
5. UVa 10284 - Chessboard in FEN * (FEN = Forsyth-Edwards Notation
is a standard notation for describing board positions in a chess game)
6. UVa 10849 - Move the bishop (chess)
7. UVa 11494 - Queen (ad hoc, chess)
• Game (Others), Easier
1. UVa 00340 - Master-Mind Hints (determine strong and weak matches)
2. UVa 00489 - Hangman Judge * (just do as asked)
3. UVa 00947 - Master Mind Helper (similar to UVa 340)
4. UVa 10189 - Minesweeper * (simulate Minesweeper, similar to UVa 10279)
5. UVa 10279 - Mine Sweeper (a 2D array helps, similar to UVa 10189)
6. UVa 10409 - Die Game (just simulate the die movement)
7. UVa 10530 - Guessing Game (use a 1D flag array)
8. UVa 11459 - Snakes and Ladders * (simulate it, similar to UVa 647)
9. UVa 12239 - Bingo (try all 902 pairs, see if all numbers in [0..N] are there)

23
1.4. THE AD HOC PROBLEMS 
c Steven & Felix

• Game (Others), Harder (more tedious)


1. UVa 00114 - Simulation Wizardry (simulation of pinball machine)
2. UVa 00141 - The Spot Game (simulation, pattern check)
3. UVa 00220 - Othello (follow the game rules, a bit tedious)
4. UVa 00227 - Puzzle (parse the input, array manipulation)
5. UVa 00232 - Crossword Answers (complex array manipulation problem)
6. UVa 00339 - SameGame Simulation (follow problem description)
7. UVa 00379 - HI-Q (follow problem description)
8. UVa 00584 - Bowling * (simulation, games, reading comprehension)
9. UVa 00647 - Chutes and Ladders (childhood board game, also see UVa 11459)
10. UVa 10363 - Tic Tac Toe (check validity of Tic Tac Toe game, tricky)
11. UVa 10443 - Rock, Scissors, Paper * (2D arrays manipulation)
12. UVa 10813 - Traditional BINGO * (follow the problem description)
13. UVa 10903 - Rock-Paper-Scissors ... (count win+losses, output win average)
• Palindrome
1. UVa 00353 - Pesky Palindromes (brute force all substring)
2. UVa 00401 - Palindromes * (simple palindrome check)
3. UVa 10018 - Reverse and Add (ad hoc, math, palindrome check)
4. UVa 10945 - Mother Bear * (palindrome)
5. UVa 11221 - Magic Square Palindrome * (we deal with a matrix)
6. UVa 11309 - Counting Chaos (palindrome check)
• Anagram
1. UVa 00148 - Anagram Checker (uses backtracking)
2. UVa 00156 - Ananagram * (easier with algorithm::sort)
3. UVa 00195 - Anagram * (easier with algorithm::next permutation)
4. UVa 00454 - Anagrams * (anagram)
5. UVa 00630 - Anagrams (II) (ad hoc, string)
6. UVa 00642 - Word Amalgamation (go through the given small dictionary for
the list of possible anagrams)
7. UVa 10098 - Generating Fast, Sorted ... (very similar to UVa 195)
• Interesting Real Life Problems, Easier
1. UVa 00161 - Traffic Lights * (this is a typical situation on the road)
2. UVa 00187 - Transaction Processing (an accounting problem)
3. UVa 00362 - 18,000 Seconds Remaining (typical file download situation)
4. UVa 00637 - Booklet Printing * (application in printer driver software)
5. UVa 00857 - Quantiser (MIDI, application in computer music)
6. UVa 10082 - WERTYU (this typographical error happens to us sometimes)
7. UVa 10191 - Longest Nap (you may want to apply this to your own schedule)
8. UVa 10528 - Major Scales (music knowledge is in the problem description)
9. UVa 10554 - Calories from Fat (are you concerned with your weights?)
10. UVa 10812 - Beat the Spread * (be careful with boundary cases!)
11. UVa 11530 - SMS Typing (handphone users encounter this problem everyday)
12. UVa 11945 - Financial Management (a bit output formatting)
13. UVa 11984 - A Change in Thermal Unit (F◦ to C◦ conversion and vice versa)
14. UVa 12195 - Jingle Composing (count the number of correct measures)
15. UVa 12555 - Baby Me (one of the first question asked when a new baby is
born; requires a bit of input processing)

24
CHAPTER 1. INTRODUCTION 
c Steven & Felix

• Interesting Real Life Problems, Harder (more tedious)


1. UVa 00139 - Telephone Tangles (calculate phone bill; string manipulation)
2. UVa 00145 - Gondwanaland Telecom (similar nature with UVa 139)
3. UVa 00333 - Recognizing Good ISBNs (note: this problem has ‘buggy’ test
data with blank lines that potentially cause lots of ‘Presentation Errors’)
4. UVa 00346 - Getting Chorded (musical chord, major/minor)
5. UVa 00403 - Postscript * (emulation of printer driver, tedious)
6. UVa 00447 - Population Explosion (life simulation model)
7. UVa 00448 - OOPS (tedious ‘hexadecimal’ to ‘assembly language’ conversion)
8. UVa 00449 - Majoring in Scales (easier if you have a musical background)
9. UVa 00457 - Linear Cellular Automata (simplified ‘game of life’ simulation;
similar idea with UVa 447; explore the Internet for that term)
10. UVa 00538 - Balancing Bank Accounts (the problem’s premise is quite true)
11. UVa 00608 - Counterfeit Dollar * (classical problem)
12. UVa 00706 - LC-Display (what we see in old digital display)
13. UVa 01061 - Consanguine Calculations * (LA 3736 - WorldFinals Tokyo07,
consanguine = blood; this problem asks possible combinations of blood types
and Rh factor; solvable by trying all eight possible blood + Rh types with
the information given in the problem description)
14. UVa 10415 - Eb Alto Saxophone Player (about musical instruments)
15. UVa 10659 - Fitting Text into Slides (typical presentation programs do this)
16. UVa 11223 - O: dah, dah, dah (tedious morse code conversion)
17. UVa 11743 - Credit Check (Luhn’s algorithm to check credit card numbers;
search the Internet to learn more)
18. UVa 12342 - Tax Calculator (tax computation can be tricky indeed)
• Time
1. UVa 00170 - Clock Patience (simulation, time)
2. UVa 00300 - Maya Calendar (ad hoc, time)
3. UVa 00579 - Clock Hands * (ad hoc, time)
4. UVa 00893 - Y3K * (use Java GregorianCalendar; similar to UVa 11356)
5. UVa 10070 - Leap Year or Not Leap ... (more than ordinary leap years)
6. UVa 10339 - Watching Watches (find the formula)
7. UVa 10371 - Time Zones (follow the problem description)
8. UVa 10683 - The decadary watch (simple clock system conversion)
9. UVa 11219 - How old are you? (be careful with boundary cases!)
10. UVa 11356 - Dates (very easy if you use Java GregorianCalendar)
11. UVa 11650 - Mirror Clock (some mathematics required)
12. UVa 11677 - Alarm Clock (similar idea with UVa 11650)
13. UVa 11947 - Cancer or Scorpio * (easier with Java GregorianCalendar)
14. UVa 11958 - Coming Home (be careful with ‘past midnight’)
15. UVa 12019 - Doom’s Day Algorithm (Gregorian Calendar; get DAY OF WEEK)
16. UVa 12136 - Schedule of a Married Man (LA 4202, Dhaka08, check time)
17. UVa 12148 - Electricity (easy with Gregorian Calendar; use method ‘add’ to
add one day to previous date and see if it is the same as the current date)
18. UVa 12439 - February 29 (inclusion-exclusion; lots of corner cases; be careful)
19. UVa 12531 - Hours and Minutes (angles between two clock hands)

25
1.4. THE AD HOC PROBLEMS 
c Steven & Felix

• ‘Time Waster’ Problems


1. UVa 00144 - Student Grants (simulation)
2. UVa 00214 - Code Generation (just simulate the process; be careful with
subtract (-), divide (/), and negate (@), tedious)
3. UVa 00335 - Processing MX Records (simulation)
4. UVa 00337 - Interpreting Control ... (simulation, output related)
5. UVa 00349 - Transferable Voting (II) (simulation)
6. UVa 00381 - Making the Grade (simulation)
7. UVa 00405 - Message Routing (simulation)
8. UVa 00556 - Amazing * (simulation)
9. UVa 00603 - Parking Lot (simulate the required process)
10. UVa 00830 - Shark (very hard to get AC, one minor error = WA)
11. UVa 00945 - Loading a Cargo Ship (simulate the given cargo loading process)
12. UVa 10033 - Interpreter (adhoc, simulation)
13. UVa 10134 - AutoFish (must be very careful with details)
14. UVa 10142 - Australian Voting (simulation)
15. UVa 10188 - Automated Judge Script (simulation)
16. UVa 10267 - Graphical Editor (simulation)
17. UVa 10961 - Chasing After Don Giovanni (tedious simulation)
18. UVa 11140 - Little Ali’s Little Brother (ad hoc)
19. UVa 11717 - Energy Saving Micro... (tricky simulation)
20. UVa 12060 - All Integer Average * (LA 3012, Dhaka04, output format)
21. UVa 12085 - Mobile Casanova * (LA 2189, Dhaka06, watch out for PE)
22. UVa 12608 - Garbage Collection (simulation with several corner cases)

26
CHAPTER 1. INTRODUCTION 
c Steven & Felix

1.5 Solutions to Non-Starred Exercises


Exercise 1.1.1: A simple test case to break greedy algorithms is N = 2, {(2, 0), (2, 1), (0, 0),
(4, 0)}. A greedy algorithm will incorrectly pair {(2, 0), (2, 1)} and {(0, 0), (4, 0)} with a 5.000
cost while the optimal solution is to pair {(0, 0), (2, 0)} and {(2, 1), (4, 0)} with cost 4.236.
Exercise 1.1.2: For a Naı̈ve Complete Search like the one outlined in the body text, one
needs up to 16 C2 ×14 C2 × . . . ×2 C2 for the largest test case with N = 8—far too large.
However, there are ways to prune the search space so that Complete Search can still work.
For an extra challenge, attempt Exercise 1.1.3*!
Exercise 1.2.1: The complete Table 1.3 is shown below.

UVa Title Problem Type Hint


10360 Rat Attack Complete Search or DP Section 3.2
10341 Solve It Divide & Conquer (Bisection Method) Section 3.3
11292 Dragon of Loowater Greedy (Non Classical) Section 3.4
11450 Wedding Shopping DP (Non Classical) Section 3.5
10911 Forming Quiz Teams DP with bitmasks (Non Classical) Section 8.3.1
11635 Hotel Booking Graph (Decomposition: Dijkstra’s + BFS) Section 8.4
11506 Angry Programmer Graph (Min Cut/Max Flow) Section 4.6
10243 Fire! Fire!! Fire!!! DP on Tree (Min Vertex Cover) Section 4.7.1
10717 Mint Decomposition: Complete Search + Math Section 8.4
11512 GATTACA String (Suffix Array, LCP, LRS) Section 6.6
10065 Useless Tile Packers Geometry (Convex Hull + Area of Polygon) Section 7.3.7

Exercise 1.2.2: The answers are:

1. (b) Use a priority queue data structure (heap) (Section 2.3).

2. (b) Use 2D Range Sum Query (Section 3.5.2).

3. If list L is static, (b) Simple Array that is pre-processed with Dynamic Programming
(Section 2.2 & 3.5). If list L is dynamic, then (g) Fenwick Tree is a better answer
(easier to implement than (f) Segment Tree).

4. (a) Yes, a complete search is possible (Section 3.2).

5. (a) O(V + E) Dynamic Programming (Section 3.5, 4.2.5, & 4.7.1).


However, (c) O((V + E) log V ) Dijkstra’s algorithm is also possible since the extra
O(log V ) factor is still ‘small’ for V up to 100K.

6. (a) Sieve of Eratosthenes (Section 5.5.1).

7. (b) The naı̈ve approach above will not work. We must (prime) factorize n! and m and
see if the (prime) factors of m can be found in the factors of n! (Section 5.5.5).

8. (b) No, we must find another way. First, find the Convex Hull of the N points in
O(n log n) (Section 7.3.7). Let the number of points in CH(S) = k. As the points are
randomly scattered, k will be much smaller than N. Then, find the two farthest points
by examining all pairs of points in the CH(S) in O(k 2 ).

9. (b) The naı̈ve approach is too slow. Use KMP or Suffix Array (Section 6.4 or 6.6)!

27
1.5. SOLUTIONS TO NON-STARRED EXERCISES 
c Steven & Felix

Exercise 1.2.3: The Java code is shown below:

// Java code for task 1, assuming all necessary imports have been done
class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
double d = sc.nextDouble();
System.out.printf("%7.3f\n", d); // yes, Java has printf too!
} }

// C++ code for task 2, assuming all necessary includes have been done
int main() {
double pi = 2 * acos(0.0); // this is a more accurate way to compute pi
int n; scanf("%d", &n);
printf("%.*lf\n", n, pi); // this is the way to manipulate field width
}

// Java code for task 3, assuming all necessary imports have been done
class Main {
public static void main(String[] args) {
String[] names = new String[]
{ "", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
Calendar calendar = new GregorianCalendar(2010, 7, 9); // 9 August 2010
// note that month starts from 0, so we need to put 7 instead of 8
System.out.println(names[calendar.get(Calendar.DAY_OF_WEEK)]); // "Wed"
} }

// C++ code for task 4, assuming all necessary includes have been done
#define ALL(x) x.begin(), x.end()
#define UNIQUE(c) (c).resize(unique(ALL(c)) - (c).begin())

int main() {
int a[] = {1, 2, 2, 2, 3, 3, 2, 2, 1};
vector<int> v(a, a + 9);
sort(ALL(v)); UNIQUE(v);
for (int i = 0; i < (int)v.size(); i++) printf("%d\n", v[i]);
}

// C++ code for task 5, assuming all necessary includes have been done
typedef pair<int, int> ii; // we will utilize the natural sort order
typedef pair<int, ii> iii; // of the primitive data types that we paired

int main() {
iii A = make_pair(ii(5, 24), -1982); // reorder DD/MM/YYYY
iii B = make_pair(ii(5, 24), -1980); // to MM, DD,
iii C = make_pair(ii(11, 13), -1983); // and then use NEGATIVE YYYY
vector<iii> birthdays;
birthdays.push_back(A); birthdays.push_back(B); birthdays.push_back(C);
sort(birthdays.begin(), birthdays.end()); // that’s all :)
}

28
CHAPTER 1. INTRODUCTION 
c Steven & Felix

// C++ code for task 6, assuming all necessary includes have been done
int main() {
int n = 5, L[] = {10, 7, 5, 20, 8}, v = 7;
sort(L, L + n);
printf("%d\n", binary_search(L, L + n, v));
}

// C++ code for task 7, assuming all necessary includes have been done
int main() {
int p[10], N = 10; for (int i = 0; i < N; i++) p[i] = i;
do {
for (int i = 0; i < N; i++) printf("%c ", ’A’ + p[i]);
printf("\n");
}
while (next_permutation(p, p + N));
}

// C++ code for task 8, assuming all necessary includes have been done
int main() {
int p[20], N = 20;
for (int i = 0; i < N; i++) p[i] = i;
for (int i = 0; i < (1 << N); i++) {
for (int j = 0; j < N; j++)
if (i & (1 << j)) // if bit j is on
printf("%d ", p[j]); // this is part of set
printf("\n");
} }

// Java code for task 9, assuming all necessary imports have been done
class Main {
public static void main(String[] args) {
String str = "FF"; int X = 16, Y = 10;
System.out.println(new BigInteger(str, X).toString(Y));
} }

// Java code for task 10, assuming all necessary imports have been done
class Main {
public static void main(String[] args) {
String S = "line: a70 and z72 will be replaced, aa24 and a872 will not";
System.out.println(S.replaceAll("(^| )+[a-z][0-9][0-9]( |$)+", " *** "));
} }

// Java code for task 11, assuming all necessary imports have been done
class Main {
public static void main(String[] args) throws Exception {
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("JavaScript"); // "cheat"
Scanner sc = new Scanner(System.in);
while (sc.hasNextLine()) System.out.println(engine.eval(sc.nextLine()));
} }

29
1.5. SOLUTIONS TO NON-STARRED EXERCISES 
c Steven & Felix

Exercise 1.2.4: Situational considerations are in brackets:


1. You receive a WA verdict for a very easy problem. What should you do?
(a) Abandon this problem for another. (Not ok, your team will lose out.)
(b) Improve the performance of your solution. (Not useful.)
(c) Create tricky test cases to find the bug. (The most logical answer.)
(d) (In team contests): Ask your team mate to re-do the problem. (This could
be feasible as you might have had some wrong assumptions about the
problem. Thus, you should refrain from telling the details about the
problem to your team mate who will re-do the problem. Still, your
team will lose precious time.)

2. You receive a TLE verdict for your O(N 3 ) solution.


However, the maximum N is just 100. What should you do?
(a) Abandon this problem for another. (Not ok, your team will lose out.)
(b) Improve the performance of your solution. (Not ok, we should not get TLE
with an O(N 3 ) algorithm if N ≤ 400.)
(c) Create tricky test cases to find the bug. (This is the answer—maybe your
program runs into an accidental infinite loop in some test cases.)

3. Follow up to Question 2: What if the maximum N is 100.000?


(If N > 400, you may have no choice but to improve the performance of the
current algorithm or use a another faster algorithm.)
4. Another follow up to Question 2: What if the maximum N is 1.000, the output only
depends on the size of input N, and you still have four hours of competition time left?
(If the output only depends on N, you may be able to pre-calculate all pos-
sible solutions by running your O(N 3 ) algorithm in the background, letting
your team mate use the computer first. Once your O(N 3 ) solution termi-
nates, you have all the answers. Submit the O(1) answer instead if it does
not exceed ‘source code size limit’ imposed by the judge.)
5. You receive an RTE verdict. Your code (seems to) execute perfectly on your machine.
What should you do?
(The most common causes of RTEs are usually array sizes that are too
small or stack overflow/infinite recursion errors. Design test cases that can
trigger these errors in your code.)
6. Thirty minutes into the contest, you take a glance at the leader board. There are many
other teams that have solved a problem X that your team has not attempted. What
should you do?
(One team member should immediately attempt problem X as it may be
relatively easy. Such a situation is really a bad news for your team as it is
a major set-back to getting a good rank in the contest.)
7. Midway through the contest, you take a glance at the leader board. The leading team
(assume that it is not your team) has just solved problem Y . What should you do?
(If your team is not the ‘pace-setter’, then it is a good idea to ‘ignore’ what
the leading team is doing and concentrate instead on solving the problems
that your team has identified to be ‘solvable’. By mid-contest your team
must have read all the problems in the problem set and roughly identified
the problems solvable with your team’s current abilities.)

30
CHAPTER 1. INTRODUCTION 
c Steven & Felix

8. Your team has spent two hours on a nasty problem. You have submitted several im-
plementations by different team members. All submissions have been judged incorrect.
You have no idea what’s wrong. What should you do?
(It is time to give up solving this problem. Do not hog the computer, let
your team mate solves another problem. Either your team has really mis-
understood the problem or in a very rare case, the judge solution is actually
wrong. In any case, this is not a good situation for your team.)
9. There is one hour to go before the end of the contest. You have 1 WA code and 1 fresh
idea for another problem. What should you (or your team) do?
(In chess terminology, this is called the ‘end game’ situation.)
(a) Abandon the problem with the WA code, switch to the other problem in an
attempt to solve one more problem.(OK in individual contests like IOI.)
(b) Insist that you have to debug the WA code. There is not enough time to start
working on a new problem. (If the idea for another problem involves com-
plex and tedious code, then deciding to focus on the WA code may be
a good idea rather than having two incomplete/‘non AC’ solutions.)
(c) (In ICPC): Print the WA code. Ask two other team members to scrutinize it while
you switch to that other problem in an attempt to solve two more problems.
(If the solution for the other problem can be coded in less than 30
minutes, then implement it while your team mates try to find the bug
for the WA code by studying the printed copy.)

Figure 1.4: Some references that inspired the authors to write this book

31
1.6. CHAPTER NOTES 
c Steven & Felix

1.6 Chapter Notes


This chapter, as well as subsequent chapters are supported by many textbooks (see Figure
1.4 in the previous page) and Internet resources. Here are some additional references:

• To improve your typing skill as mentioned in Tip 1, you may want to play the many
typing games available online.
• Tip 2 is adapted from the introduction text in USACO training gateway [48].
• More details about Tip 3 can be found in many CS books, e.g. Chapter 1-5, 17 of [7].
• Online references for Tip 4:
http://www.cppreference.com and http://www.sgi.com/tech/stl/ for C++ STL;
http://docs.oracle.com/javase/7/docs/api/ for Java API.
You do not have to memorize all library functions, but it is useful to memorize functions
that you frequently use.
• For more insights on better testing (Tip 5), a slight detour to software engineering
books may be worth trying.
• There are many other Online Judges apart from those mentioned in Tip 6, e.g.

– Codeforces, http://codeforces.com/,
– Peking University Online Judge, (POJ) http://poj.org,
– Zhejiang University Online Judge, (ZOJ) http://acm.zju.edu.cn,
– Tianjin University Online Judge, http://acm.tju.edu.cn/toj,
– Ural State University (Timus) Online Judge, http://acm.timus.ru,
– URI Online Judge, http://www.urionlinejudge.edu.br, etc.

• For a note regarding team contest (Tip 7), read [16].

In this chapter, we have introduced the world of competitive programming to you. However,
a competitive programmer must be able to solve more than just Ad Hoc problems in a
programming contest. We hope that you will enjoy the ride and fuel your enthusiasm by
reading up on and learning new concepts in the other chapters of this book. Once you have
finished reading the book, re-read it once more. On the second time, attempt and solve the
≈ 238 written exercises and the ≈ 1675 programming exercises.

Statistics First Edition Second Edition Third Edition


Number of Pages 13 19 (+46%) 32 (+68%)
Written Exercises 4 4 6+3*=9 (+125%)
Programming Exercises 34 160 (+371%) 173 (+8%)

32
Chapter 2

Data Structures and Libraries

If I have seen further it is only by standing on the shoulders of giants.


— Isaac Newton

2.1 Overview and Motivation


A data structure (DS) is a means of storing and organizing data. Different data structures
have different strengths. So when designing an algorithm, it is important to pick one that
allows for efficient insertions, searches, deletions, queries, and/or updates, depending on what
your algorithm needs. Although a data structure does not in itself solve a (programming
contest) problem (the algorithm operating on it does), using an appropriately efficient data
structure for a problem may be the difference between passing or exceeding the problem’s
time limit. There can be many ways to organize the same data and sometimes one way is
better than the other in some contexts. We will illustrate this several times in this chapter.
A keen familiarity with the data structures and libraries discussed in this chapter is critically
important for understanding the algorithms that use them in subsequent chapters.
As stated in the preface of this book, we assume that you are familiar with the basic
data structures listed in Section 2.2-2.3 and thus we will not review them in this book.
Instead, we will simply highlight the fact that there exist built-in implementations for these
elementary data structures in the C++ STL and Java API1 . If you feel that you are not
entirely familiar with any of the terms or data structures mentioned in Section 2.2-2.3,
please review those particular terms and concepts in the various reference books2 that cover
them, including classics such as the “Introduction to Algorithms” [7], “Data Abstraction and
Problem Solving” [5, 54], “Data Structures and Algorithms” [12], etc. Continue reading this
book only when you understand at least the basic concepts behind these data structures.
Note that for competitive programming, you only need to know enough about these data
structures to be able to select and to use the correct data structures for each given contest
problem. You should understand the strengths, weaknesses, and time/space complexities of
typical data structures. The theory behind them is definitely good reading, but can often
be skipped or skimmed through, since the built-in libraries provide ready-to-use and highly
reliable implementations of otherwise complex data structures. This is not a good practice,
but you will find that it is often sufficient. Many (younger) contestants have been able
to utilize the efficient (with a O(log n) complexity for most operations) C++ STL map (or
1
Even in this third edition, we still primarily use C++ code to illustrate implementation techniques. The
Java equivalents can be found in the supporting website of this book.
2
Materials in Section 2.2-2.3 are usually covered in year one Data Structures CS curriculae. High school
students aspiring to take part in the IOI are encouraged to engage in independent study on such material.

33
2.1. OVERVIEW AND MOTIVATION 
c Steven & Felix

Java TreeMap) implementations to store dynamic collections of key-data pairs without an


understanding that the underlying data structure is a balanced Binary Search Tree, or use
the C++ STL priority queue (or Java PriorityQueue) to order a queue of items without
understanding that the underlying data structure is a (usually Binary) Heap. Both data
structures are typically taught in year one Computer Science curriculae.
This chapter is divided into three parts. Section 2.2 contains basic linear data structures
and the basic operations they support. Section 2.3 covers basic non-linear data structures
such as (balanced) Binary Search Trees (BST), (Binary) Heaps, and Hash Tables, as well
as their basic operations. The discussion of each data structure in Section 2.2-2.3 is brief,
with an emphasis on the important library routines that exist for manipulating the data
structures. However, special data structures that are common in programming contests,
such as bitmask and several bit manipulation techniques (see Figure 2.1) are discussed in
more detail. Section 2.4 contains more data structures for which there exist no built-in
implementation, and thus require us to build our own libraries. Section 2.4 has a more
in-depth discussion than Section 2.2-2.3.

Value-Added Features of this Book


As this chapter is the first that dives into the heart of competitive programming, we will
now take the opportunity to highlight several value-added features of this book that you will
see in this and the following chapters.
A key feature of this book is its accompanying collection of efficient, fully-implemented
examples in both C/C++ and Java that many other Computer Science books lack, stop-
ping at the ‘pseudo-code level’ in their demonstration of data structures and algorithms.
This feature has been in the book since the very first edition. The important parts of
the source code have been included in the book3 and the full source code is hosted at
sites.google.com/site/stevenhalim/home/material. The reference to each source file
is indicated in the body text as a box like the one shown below.

Source code: chx yy name.cpp/java

Another strength of this book is the collection of both written and programming exercises
(mostly supported by the UVa Online Judge [47] and integrated with uHunt—see Appendix
A). In the third edition, we have added many more written exercises. We have classified the
written exercises into non-starred and starred ones. The non-starred written exercises are
meant to be used mainly for self-checking purposes; solutions are given at the back of each
chapter. The starred written exercises can be used for extra challenges; we do not provide
solutions for these but may instead provide some helpful hints.
In the third edition, we have added visualizations4 for many data structures and algo-
rithms covered in this book [27]. We believe that these visualizations will be a huge benefit
to the visual learners in our reader base. At this point in time (24 May 2013), the visualiza-
tions are hosted at: www.comp.nus.edu.sg/∼stevenha/visualization. The reference to
each visualization is included in the body text as a box like the one shown below.

Visualization: www.comp.nus.edu.sg/∼stevenha/visualization

3
However, we have chosen not to include code from Section 2.2-2.3 in the body text because they are
mostly ‘trivial’ for many readers, except perhaps for a few useful tricks.
4
They are built with HTML5 canvas and JavaScript technology.

34
CHAPTER 2. DATA STRUCTURES AND LIBRARIES 
c Steven & Felix

2.2 Linear DS with Built-in Libraries


A data structure is classified as a linear data structure if its elements form a linear sequence,
i.e. its elements are arranged from left to right (or top to bottom). Mastery of these basic
linear data structures below is critical in today’s programming contests.
• Static Array (native support in both C/C++ and Java)
This is clearly the most commonly used data structure in programming contests.
Whenever there is a collection of sequential data to be stored and later accessed us-
ing their indices, the static array is the most natural data structure to use. As the
maximum input size is usually mentioned in the problem statement, the array size
can be declared to be the maximum input size, with a small extra buffer (sentinel) for
safety—to avoid the unnecessary ‘off by one’ RTE. Typically, 1D, 2D, and 3D arrays
are used in programming contests—problems rarely require arrays of higher dimension.
Typical array operations include accessing elements by their indices, sorting elements,
performing a linear scan or a binary search on a sorted array.
• Dynamically-Resizeable Array: C++ STL vector (Java ArrayList (faster) or Vector)
This data structure is similar to the static array, except that it is designed to handle
runtime resizing natively. It is better to use a vector in place of an array if the size
of the sequence of elements is unknown at compile-time. Usually, we initialize the size
(reserve() or resize()) with the estimated size of the collection for better perfor-
mance. Typical C++ STL vector operations used in competitive programming include
push back(), at(), the [] operator, assign(), clear(), erase(), and iterators for
traversing the contents of vectors.

Source code: ch2 01 array vector.cpp/java

It is appropriate to discuss two operations commonly performed on Arrays: Sorting


and Searching. These two operations are well supported in C++ and Java.
There are many sorting algorithms mentioned in CS books [7, 5, 54, 12, 40, 58], e.g.

1. O(n2) comparison-based sorting algorithms: Bubble/Selection/Insertion Sort, etc.


These algorithms are (awfully) slow and usually avoided in programming contests,
though understanding them might help you solve certain problems.
2. O(n log n) comparison-based sorting algorithms: Merge/Heap/Quick Sort, etc.
These algorithms are the default choice in programming contests as an O(n log n)
complexity is optimal for comparison-based sorting. Therefore, these sorting algo-
rithms run in the ‘best possible’ time in most cases (see below for special purpose
sorting algorithms). In addition, these algorithms are well-known and hence we
do not need to ‘reinvent the wheel’5 —we can simply use sort, partial sort, or
stable sort in C++ STL algorithm (or Collections.sort in Java) for stan-
dard sorting tasks. We only need to specify the required comparison function and
these library routines will handle the rest.
3. Special purpose sorting algorithms: O(n) Counting/Radix/Bucket Sort, etc.
Although rarely used, these special purpose algorithms are good to know as they
can reduce the required sorting time if the data has certain special characteristics.
For example, Counting Sort can be applied to integer data that lies in a small
range (see Section 9.32).
5
However, sometimes we do need to ‘reinvent the wheel’ for certain sorting-related problems, e.g. the
Inversion Index problem in Section 9.14.

35
2.2. LINEAR DS WITH BUILT-IN LIBRARIES 
c Steven & Felix

There are generally three common methods to search for an item in an array:

1. O(n) Linear Search: Consider every element from index 0 to index n − 1 (avoid
this whenever possible).
2. O(log n) Binary Search: Use lower bound, upper bound, or binary search in
C++ STL algorithm (or Java Collections.binarySearch). If the input array is
unsorted, it is necessary to sort the array at least once (using one of the O(n log n)
sorting algorithm above) before executing one (or many) Binary Search(es).
3. O(1) with Hashing: This is a useful technique to use when fast access to known
values are required. If a suitable hash function is selected, the probability of a
collision to be made is negligibly small. Still, this technique is rarely used and we
can live without it6 for most (contest) problems.

Visualization: www.comp.nus.edu.sg/∼stevenha/visualization/sorting.html
Source code: ch2 02 algorithm collections.cpp/java

• Array of Booleans: C++ STL bitset (Java BitSet)


If our array needs only to contain Boolean values (1/true and 0/false), we can use
an alternative data structure other than an array—a C++ STL bitset. The bitset
supports useful operations like reset(), set(), the [] operator and test().

Source code: ch5 06 primes.cpp/java, also see Section 5.5.1

• Bitmasks a.k.a. lightweight, small sets of Booleans (native support in C/C++/Java)


An integer is stored in a computer’s memory as a sequence/string of bits. Thus, we can
use integers to represent a lightweight small set of Boolean values. All set operations
then involve only the bitwise manipulation of the corresponding integer, which makes
it a much more efficient choice when compared with the C++ STL vector<bool>,
bitset, or set<int> options. Such speed is important in competitive programming.
Some important operations that are used in this book are shown below.

Figure 2.1: Bitmask Visualization

1. Representation: A 32 (or 64)-bit signed integer for up to 32 (or 64) items7 . With-
out a loss of generality, all examples below use a 32-bit signed integer called S.
6
However, questions about hashing frequently appear in interviews for IT jobs.
7
To avoid issues with the two’s complement representation, use a 32-bit/64-bit signed integer to represent
bitmasks of up to 30/62 items only, respectively.

36
CHAPTER 2. DATA STRUCTURES AND LIBRARIES 
c Steven & Felix

Example: 5| 4| 3| 2| 1| 0 <- 0-based indexing from right


32|16| 8| 4| 2| 1 <- power of 2
S = 34 (base 10) = 1| 0| 0| 0| 1| 0 (base 2)
F| E| D| C| B| A <- alternative alphabet label

In the example above, the integer S = 34 or 100010 in binary also represents a


small set {1, 5} with a 0-based indexing scheme in increasing digit significance
(or {B, F} using the alternative alphabet label) because the second and the sixth
bits (counting from the right) of S are on.
2. To multiply/divide an integer by 2, we only need to shift the bits in the integer
left/right, respectively. This operation (especially the shift left operation) is im-
portant for the next few examples below. Notice that the truncation in the shift
right operation automatically rounds the division-by-2 down, e.g. 17/2 = 8.

S = 34 (base 10) = 100010 (base 2)


S = S << 1 = S * 2 = 68 (base 10) = 1000100 (base 2)
S = S >> 2 = S / 4 = 17 (base 10) = 10001 (base 2)
S = S >> 1 = S / 2 = 8 (base 10) = 1000 (base 2) <- LSB is gone
(LSB = Least Significant Bit)

3. To set/turn on the j-th item (0-based indexing) of the set,


use the bitwise OR operation S |= (1 << j).

S = 34 (base 10) = 100010 (base 2)


j = 3, 1 << j = 001000 <- bit ‘1’ is shifted to the left 3 times
-------- OR (true if either of the bits is true)
S = 42 (base 10) = 101010 (base 2) // update S to this new value 42

4. To check if the j-th item of the set is on,


use the bitwise AND operation T = S & (1 << j).
If T = 0, then the j-th item of the set is off.
If T != 0 (to be precise, T = (1 << j)), then the j-th item of the set is on.
See Figure 2.1 for one such example.

S = 42 (base 10) = 101010 (base 2)


j = 3, 1 << j = 001000 <- bit ‘1’ is shifted to the left 3 times
-------- AND (only true if both bits are true)
T = 8 (base 10) = 001000 (base 2) -> not zero, the 3rd item is on

S = 42 (base 10) = 101010 (base 2)


j = 2, 1 << j = 000100 <- bit ‘1’ is shifted to the left 2 times
-------- AND
T = 0 (base 10) = 000000 (base 2) -> zero, the 2rd item is off

5. To clear/turn off the j-th item of the set,


use8 the bitwise AND operation S &= ∼(1 << j).

S = 42 (base 10) = 101010 (base 2)


j = 1, ~(1 << j) = 111101 <- ‘~’ is the bitwise NOT operation
-------- AND
S = 40 (base 10) = 101000 (base 2) // update S to this new value 40
8
Use brackets a lot when doing bit manipulation to avoid accidental bugs due to operator precedence.

37
2.2. LINEAR DS WITH BUILT-IN LIBRARIES 
c Steven & Felix

6. To toggle (flip the status of) the j-th item of the set,
use the bitwise XOR operation S ∧ = (1 << j).

S = 40 (base 10) = 101000 (base 2)


j = 2, (1 << j) = 000100 <- bit ‘1’ is shifted to the left 2 times
-------- XOR <- true if both bits are different
S = 44 (base 10) = 101100 (base 2) // update S to this new value 44

S = 40 (base 10) = 101000 (base 2)


j = 3, (1 << j) = 001000 <- bit ‘1’ is shifted to the left 3 times
-------- XOR <- true if both bits are different
S = 32 (base 10) = 100000 (base 2) // update S to this new value 32

7. To get the value of the least significant bit that is on (first from the right),
use T = (S & (-S)).

S = 40 (base 10) = 000...000101000 (32 bits, base 2)


-S = -40 (base 10) = 111...111011000 (two’s complement)
----------------- AND
T = 8 (base 10) = 000...000001000 (3rd bit from right is on)

8. To turn on all bits in a set of size n, use S = (1 << n) - 1


(be careful with overflows).

Example for n = 3
S + 1 = 8 (base 10) = 1000 <- bit ‘1’ is shifted to left 3 times
1
------ -
S = 7 (base 10) = 111 (base 2)

Example for n = 5
S + 1 = 32 (base 10) = 100000 <- bit ‘1’ is shifted to left 5 times
1
-------- -
S = 31 (base 10) = 11111 (base 2)

Visualization: www.comp.nus.edu.sg/∼stevenha/visualization/bitmask.html
Source code: ch2 03 bit manipulation.cpp/java

Many bit manipulation operations are written as preprocessor macros in our C/C++
example source code (but written plainly in our Java example code since Java does
not support macros).

• Linked List: C++ STL list (Java LinkedList)


Although this data structure almost always appears in data structure and algorithm
textbooks, the Linked List is usually avoided in typical (contest) problems. This is due
to the inefficiency in accessing elements (a linear scan has to be performed from the
head or the tail of a list) and the usage of pointers makes it prone to runtime errors
if not implemented properly. In this book, almost all forms of Linked List have been
replaced by the more flexible C++ STL vector (Java Vector).

38
CHAPTER 2. DATA STRUCTURES AND LIBRARIES 
c Steven & Felix

The only exception is probably UVa 11988 - Broken Keyboard (a.k.a. Beiju Text)—
where you are required to dynamically maintain a (linked) list of characters and effi-
ciently insert a new character anywhere in the list, i.e. at front (head), current, or back
(tail) of the (linked) list. Out of 1903 UVa problems that the authors have solved, this
is likely to be the only pure linked list problem we have encountered thus far.

• Stack: C++ STL stack (Java Stack)


This data structure is often used as part of algorithms that solve certain problems (e.g.
bracket matching in Section 9.4, Postfix calculator and Infix to Postfix conversion in
Section 9.27, finding Strongly Connected Components in Section 4.2.9 and Graham’s
scan in Section 7.3.7). A stack only allows for O(1) insertion (push) and O(1) deletion
(pop) from the top. This behavior is usually referred to as Last In First Out (LIFO) and
is reminiscent of literal stacks in the real world. Typical C++ STL stack operations
include push()/pop() (insert/remove from top of stack), top() (obtain content from
the top of stack), and empty().

• Queue: C++ STL queue (Java Queue9 )


This data structure is used in algorithms like Breadth First Search (BFS) in Section
4.2.2. A queue only allows for O(1) insertion (enqueue) from the back (tail) and O(1)
deletion (dequeue) from the front (head). This behavior is similarly referred to as
First In First Out (FIFO), just like actual queues in the real world. Typical C++
STL queue operations include push()/pop() (insert from back/remove from front of
queue), front()/back() (obtain content from the front/back of queue), and empty().

• Double-ended Queue (Deque): C++ STL deque (Java Deque10 )


This data structure is very similar to the resizeable array (vector) and queue above,
except that deques support fast O(1) insertions and deletions at both the beginning
and the end of the deque. This feature is important in certain algorithm, e.g. the
Sliding Window algorithm in Section 9.31. Typical C++ STL deque operations in-
clude push back(), pop front() (just like the normal queue), push front() and
pop back() (specific for deque).

Visualization: www.comp.nus.edu.sg/∼stevenha/visualization/list.html
Source code: ch2 04 stack queue.cpp/java

Exercise 2.2.1*: Suppose you are given an unsorted array S of n integers. Solve each
of the following tasks below with the best possible algorithms that you can think of and
analyze their time complexities. Let’s assume the following constraints: 1 ≤ n ≤ 100K so
that O(n2 ) solutions are theoretically infeasible in a contest environment.
1. Determine if S contains one or more pairs of duplicate integers.
2*. Given an integer v, find two integers a, b ∈ S such that a + b = v.
3*. Follow-up to Question 2: what if the given array S is already sorted ?
4*. Print the integers in S that fall between a range [a . . . b] (inclusive) in sorted order.
5*. Determine the length of the longest increasing contiguous sub-array in S.
6. Determine the median (50th percentile) of S. Assume that n is odd.
9
The Java Queue is only an interface that is usually instantiated with Java LinkedList.
10
The Java Deque is also an interface. Deque is usually instantiated with Java LinkedList.

39
2.2. LINEAR DS WITH BUILT-IN LIBRARIES 
c Steven & Felix

Exercise 2.2.2: There are several other ‘cool’ tricks possible with bit manipulation tech-
niques but these are rarely used. Please implement these tasks with bit manipulation:

1. Obtain the remainder (modulo) of S when it is divided by N (N is a power of 2)


e.g. S = (7)10 % (4)10 = (111)2 % (100)2 = (11)2 = (3)10 .
2. Determine if S is a power of 2.
e.g. S = (7)10 = (111)2 is not a power of 2, but (8)10 = (100)2 is a power of 2.
3. Turn off the last bit in S, e.g. S = (40)10 = (101000)2 → S = (32)10 = (100000)2.
4. Turn on the last zero in S, e.g. S = (41)10 = (101001)2 → S = (43)10 = (101011)2 .
5. Turn off the last consecutive run of ones in S
e.g. S = (39)10 = (100111)2 → S = (32)10 = (100000)2 .
6. Turn on the last consecutive run of zeroes in S
e.g. S = (36)10 = (100100)2 → S = (39)10 = (100111)2 .
7*. Solve UVa 11173 - Grey Codes with a one-liner bit manipulation expression for each
test case, i.e. find the k-th Gray code.
8*. Let’s reverse the UVa 11173 problem above. Given a gray code, find its position k
using bit manipulation.

Exercise 2.2.3*: We can also use a resizeable array (C++ STL vector or Java Vector) to
implement an efficient stack. Figure out how to achieve this. Follow up question: Can we
use a static array, linked list, or deque instead? Why or why not?
Exercise 2.2.4*: We can use a linked list (C++ STL list or Java LinkedList) to imple-
ment an efficient queue (or deque). Figure out how to achieve this. Follow up question: Can
we use a resizeable array instead? Why or why not?

Programming exercises involving linear data structures (and algorithms) with libraries:

• 1D Array Manipulation, e.g. array, C++ STL vector (or Java Vector/ArrayList)
1. UVa 00230 - Borrowers (a bit of string parsing, see Section 6.2; maintain list
of sorted books; sort key: author names first and if ties, by title; the input
size is small although not stated; we do not need to use balanced BST)
2. UVa 00394 - Mapmaker (any n-dimensional array is stored in computer mem-
ory as a single dimensional array; follow the problem description)
3. UVa 00414 - Machined Surfaces (get longest stretch of ‘B’s)
4. UVa 00467 - Synching Signals (linear scan, 1D boolean flag)
5. UVa 00482 - Permutation Arrays (you may need to use a string tokenizer—
see Section 6.2—as the size of the array is not specified)
6. UVa 00591 - Box of Bricks (sum all items; get the average; sum the total
absolute differences of each item from the average divided by two)
7. UVa 00665 - False Coin (use 1D boolean flags; all coins are initially potential
false coins; if ‘=’, all coins on the left and right are not false coins; if ‘<’ or
‘>’, all coins not on the left and right are not false coins; check if there is
only one candidate false coin left at the end)
8. UVa 00755 - 487-3279 (Direct Addressing Table; convert the letters except
Q & Z to 2-9; keep ‘0’-‘9’ as 0-9; sort the integers; find duplicates if any)

40
CHAPTER 2. DATA STRUCTURES AND LIBRARIES 
c Steven & Felix

9. UVa 10038 - Jolly Jumpers * (use 1D boolean flags to check [1..n − 1])
10. UVa 10050 - Hartals (1D boolean flag)
11. UVa 10260 - Soundex (Direct Addressing Table for soundex code mapping)
12. UVa 10978 - Let’s Play Magic (1D string array manipulation)
13. UVa 11093 - Just Finish it up (linear scan, circular array, a bit challenging)
14. UVa 11192 - Group Reverse (character array)
15. UVa 11222 - Only I did it (use several 1D arrays to simplify this problem)
16. UVa 11340 - Newspaper * (DAT; see Hashing in Section 2.3)
17. UVa 11496 - Musical Loop (store data in 1D array, count the peaks)
18. UVa 11608 - No Problem (use three arrays: created; required; available)
19. UVa 11850 - Alaska (for each integer location from 0 to 1322; can Brenda
reach (anywhere within 200 miles of) any charging stations?)
20. UVa 12150 - Pole Position (simple manipulation)
21. UVa 12356 - Army Buddies * (similar to deletion in doubly linked lists,
but we can still use a 1D array for the underlying data structure)
• 2D Array Manipulation
1. UVa 00101 - The Blocks Problem (‘stack’ like simulation; but we need to
access the content of each stack too, so it is better to use 2D array)
2. UVa 00434 - Matty’s Blocks (a kind of visibility problem in geometry, solvable
with using 2D array manipulation)
3. UVa 00466 - Mirror Mirror (core functions: rotate and reflect)
4. UVa 00541 - Error Correction (count the number of ‘1’s for each row/col; all
of them must be even; if ∃ an error, check if it is on the same row and col)
5. UVa 10016 - Flip-flop the Squarelotron (tedious)
6. UVa 10703 - Free spots (use 2D boolean array of size 500 × 500)
7. UVa 10855 - Rotated squares * (string array, 90o clockwise rotation)
8. UVa 10920 - Spiral Tap * (simulate the process)
9. UVa 11040 - Add bricks in the wall (non trivial 2D array manipulation)
10. UVa 11349 - Symmetric Matrix (use long long to avoid issues)
11. UVa 11360 - Have Fun with Matrices (do as asked)
12. UVa 11581 - Grid Successors * (simulate the process)
13. UVa 11835 - Formula 1 (do as asked)
14. UVa 12187 - Brothers (simulate the process)
15. UVa 12291 - Polyomino Composer (do as asked, a bit tedious)
16. UVa 12398 - NumPuzz I (simulate backwards, do not forget to mod 10)
• C++ STL algorithm (Java Collections)
1. UVa 00123 - Searching Quickly (modified comparison function, use sort)
2. UVa 00146 - ID Codes * (use next permutation)
3. UVa 00400 - Unix ls (this command very frequently used in UNIX)
4. UVa 00450 - Little Black Book (tedious sorting problem)
5. UVa 00790 - Head Judge Headache (similar to UVa 10258)
6. UVa 00855 - Lunch in Grid City (sort, median)
7. UVa 01209 - Wordfish (LA 3173, Manila06) (STL next and prev permutation)
8. UVa 10057 - A mid-summer night ... (involves the median, use STL sort,
upper bound, lower bound and some checks)

41
2.2. LINEAR DS WITH BUILT-IN LIBRARIES 
c Steven & Felix

9. UVa 10107 - What is the Median? * (find median of a growing/dynamic


list of integers; still solvable with multiple calls of nth element in algorithm)
10. UVa 10194 - Football a.k.a. Soccer (multi-fields sorting, use sort)
11. UVa 10258 - Contest Scoreboard * (multi-fields sorting, use sort)
12. UVa 10698 - Football Sort (multi-fields sorting, use sort)
13. UVa 10880 - Colin and Ryan (use sort)
14. UVa 10905 - Children’s Game (modified comparison function, use sort)
15. UVa 11039 - Building Designing (use sort then count different signs)
16. UVa 11321 - Sort Sort and Sort (be careful with negative mod!)
17. UVa 11588 - Image Coding (sort simplifies the problem)
18. UVa 11777 - Automate the Grades (sort simplifies the problem)
19. UVa 11824 - A Minimum Land Price (sort simplifies the problem)
20. UVa 12541 - Birthdates (LA6148, HatYai12, sort, pick youngest and oldest)
• Bit Manipulation (both C++ STL bitset (Java BitSet) and bitmask)
1. UVa 00594 - One Little, Two Little ... (manipulate bit string with bitset)
2. UVa 00700 - Date Bugs (can be solved with bitset)
3. UVa 01241 - Jollybee Tournament (LA 4147, Jakarta08, easy)
4. UVa 10264 - The Most Potent Corner * (heavy bitmask manipulation)
5. UVa 11173 - Grey Codes (D & C pattern or one liner bit manipulation)
6. UVa 11760 - Brother Arif, ... (separate row+col checks; use two bitsets)
7. UVa 11926 - Multitasking * (use 1M bitset to check if a slot is free)
8. UVa 11933 - Splitting Numbers * (an exercise for bit manipulation)
9. IOI 2011 - Pigeons (this problem becomes simpler with bit manipulation but
the final solution requires much more than that.)
• C++ STL list (Java LinkedList)
1. UVa 11988 - Broken Keyboard ... * (rare linked list problem)
• C++ STL stack (Java Stack)
1. UVa 00127 - “Accordian” Patience (shuffling stack)
2. UVa 00514 - Rails * (use stack to simulate the process)
3. UVa 00732 - Anagram by Stack * (use stack to simulate the process)
4. UVa 01062 - Containers * (LA 3752, WorldFinals Tokyo07, simulation
with stack; maximum answer is 26 stacks; O(n) solution exists)
5. UVa 10858 - Unique Factorization (use stack to help solving this problem)
Also see: implicit stacks in recursive function calls and Postfix conver-
sion/evaluation in Section 9.27.
• C++ STL queue and deque (Java Queue and Deque)
1. UVa 00540 - Team Queue (modified ‘queue’)
2. UVa 10172 - The Lonesome Cargo ... * (use both queue and stack)
3. UVa 10901 - Ferry Loading III * (simulation with queue)
4. UVa 10935 - Throwing cards away I (simulation with queue)
5. UVa 11034 - Ferry Loading IV * (simulation with queue)
6. UVa 12100 - Printer Queue (simulation with queue)
7. UVa 12207 - This is Your Queue (use both queue and deque)
Also see: queues in BFS (see Section 4.2.2)

42
CHAPTER 2. DATA STRUCTURES AND LIBRARIES 
c Steven & Felix

2.3 Non-Linear DS with Built-in Libraries


For some problems, linear storage is not the best way to organize data. With the efficient
implementations of non-linear data structures shown below, you can operate on the data in
a quicker fashion, thereby speeding up the algorithms that rely on them.
For example, if you need a dynamic 11 collection of pairs (e.g. key → value pairs), using
C++ STL map below can provide you O(log n) performance for insertion/search/deletion
operations with just a few lines of code (that you still have to write yourself), whereas
storing the same information inside a static array of structs may require O(n) inser-
tion/search/deletion, and you will need to write the longer traversal code yourself.

• Balanced Binary Search Tree (BST): C++ STL map/set (Java TreeMap/TreeSet)
The BST is one way to organize data in a tree structure. In each subtree rooted at x,
the following BST property holds: Items on the left subtree of x are smaller than x
and items on the right subtree of x are greater than (or equal to) x. This is essentially
an application of the Divide and Conquer strategy (also see Section 3.3). Organizing
the data like this (see Figure 2.2) allows for O(log n) search(key), insert(key),
findMin()/findMax(), successor(key)/predecessor(key), and delete(key) since
in the worst case, only O(log n) operations are required in a root-to-leaf scan (see
[7, 5, 54, 12] for details). However, this only holds if the BST is balanced.

Figure 2.2: Examples of BST

Implementing bug-free balanced BSTs such as the Adelson-Velskii Landis (AVL)12 or


Red-Black (RB)13 Trees is a tedious task and is difficult to achieve in a time-constrained
contest environment (unless you have prepared a code library beforehand, see Section
9.29). Fortunately, C++ STL has map and set (and Java has TreeMap and TreeSet)
which are usually implementations of the RB Tree which guarantees that major BST
operations like insertions/searches/deletions are done in O(log n) time. By mastering
these two C++ STL template classes (or Java APIs), you can save a lot of precious
coding time during contests! The difference between these two data structures is simple:
the C++ STL map (and Java TreeMap) stores (key → data) pairs whereas the C++
11
The contents of a dynamic data structure is frequently modified via insert/delete/update operations.
12
The AVL tree was the first self-balancing BST to be invented. AVL trees are essentially traditional
BSTs with an additional property: The heights of the two subtrees of any vertex in an AVL tree can differ
by at most one. Rebalancing operations (rotations) are performed (when necessary) during insertions and
deletions to maintain this invariant property, hence keeping the tree roughly balanced.
13
The Red-Black tree is another self-balancing BST, in which every vertex has a color: red or black. In
RB trees, the root vertex, all leaf vertices, and both children of every red vertex are black. Every simple
path from a vertex to any of its descendant leaves contains the same number of black vertices. Throughout
insertions and deletions, an RB tree will maintain all these invariants to keep the tree balanced.

43
2.3. NON-LINEAR DS WITH BUILT-IN LIBRARIES 
c Steven & Felix

STL set (and Java TreeSet) only stores the key. For most (contest) problems, we use
a map (to really map information) instead of a set (a set is only useful for efficiently
determining the existence of a certain key). However, there is a small drawback. If we
use the library implementations, it becomes difficult or impossible to augment (add
extra information to) the BST. Please attempt Exercise 2.3.5* and read Section 9.29
for more details.

Visualization: www.comp.nus.edu.sg/∼stevenha/visualization/bst.html
Source code: ch2 05 map set.cpp/java

• Heap: C++ STL priority queue (Java PriorityQueue)


The heap is another way to organize data in a tree. The (Binary) Heap is also a binary
tree like the BST, except that it must be a complete 14 tree. Complete binary trees
can be stored efficiently in a compact 1-indexed array of size n + 1, which is often
preferred to an explicit tree representation. For example, the array A = {N/A, 90, 19,
36, 17, 3, 25, 1, 2, 7} is the compact array representation of Figure 2.3 with index 0
ignored. One can navigate from a certain index (vertex) i to its parent, left child, and
right child by using simple index manipulation:  2i , 2 × i, and 2 × i + 1, respectively.
These index manipulations can be made faster using bit manipulation techniques (see
Section 2.2): i >> 1, i << 1, and (i << 1) + 1, respectively.
Instead of enforcing the BST property, the (Max) Heap enforces the Heap property:
in each subtree rooted at x, items on the left and right subtrees of x are smaller than
(or equal to) x (see Figure 2.3). This is also an application of the Divide and Conquer
concept (see Section 3.3). The property guarantees that the top (or root) of the heap
is always the maximum element. There is no notion of a ‘search’ in the Heap (unlike
BSTs). The Heap instead allows for the fast extraction (deletion) of the maximum
element: ExtractMax() and insertion of new items: Insert(v)—both of which can
be easily achieved by in a O(log n) root-to-leaf or leaf-to-root traversal, performing
swapping operations to maintain the (Max) Heap property whenever necessary (see
[7, 5, 54, 12] for details).

Figure 2.3: (Max) Heap Visualization

The (Max) Heap is a useful data structure for modeling a Priority Queue, where the
item with the highest priority (the maximum element) can be dequeued (ExtractMax())
14
A complete binary tree is a binary tree in which every level, except possibly the last, is completely filled.
All vertices in the last level must also be filled from left-to-right.

44
CHAPTER 2. DATA STRUCTURES AND LIBRARIES 
c Steven & Felix

and a new item v can be enqueued (Insert(v)), both in O(log n) time. The imple-
mentation15 of priority queue is available in the C++ STL queue library (or Java
PriorityQueue). Priority Queues are an important component in algorithms like
Prim’s (and Kruskal’s) algorithms for the Minimum Spanning Tree (MST) problem
(see Section 4.3), Dijkstra’s algorithm for the Single-Source Shortest Paths (SSSP)
problem (see Section 4.4.3), and the A* Search algorithm (see Section 8.2.5).
This data structure is also used to perform partial sort in the C++ STL algorithm
library. One possible implementation is by processing the elements one by one and
creating a Max16 Heap of k elements, removing the largest element whenever its size
exceeds k (k is the number of elements requested by user). The smallest k elements
can then be obtained in descending order by dequeuing the remaining elements in the
Max Heap. As each dequeue operation is O(log k), partial sort has O(n log k) time
complexity17 . When k = n, this algorithm is equivalent to a heap sort. Note that
although the time complexity of a heap sort is also O(n log n), heap sorts are often
slower than quick sorts because heap operations access data stored in distant indices
and are thus not cache-friendly.

Visualization: www.comp.nus.edu.sg/∼stevenha/visualization/heap.html
Source code: ch2 06 priority queue.cpp/java

• Hash Table: C++11 STL unordered map18 (and Java HashMap/HashSet/HashTable)


The Hash Table is another non-linear data structure, but we do not recommend using
it in programming contests unless absolutely necessary. Designing a well-performing
hash function is often tricky and only the new C++11 has STL support for it (Java
has Hash-related classes).
Moreover, C++ STL maps or sets (and Java TreeMaps or TreeSets) are usually fast
enough as the typical input size of (programming contest) problems is usually not more
than 1M. Within these bounds, the O(1) performance of Hash Tables and O(log 1M)
performance for balanced BSTs do not differ by much. Thus, we do not discuss Hash
Tables in detail in this section.
However, a simple form of Hash Tables can be used in programming contests. ‘Di-
rect Addressing Tables’ (DATs) can be considered to be Hash Tables where the keys
themselves are the indices, or where the ‘hash function’ is the identity function. For
example, we may need to assign all possible ASCII characters [0-255] to integer values,
e.g. ‘a’ → ‘3’, ‘W’ → ‘10’, . . . , ‘I’ → ‘13’. For this purpose, we do not need the C++
STL map or any form of hashing as the key itself (the value of the ASCII character) is
unique and sufficient to determine the appropriate index in an array of size 256. Some
programming exercises involving DATs are listed in the previous Section 2.2.

15
The default C++ STL priority queue is a Max Heap (dequeuing yields items in descending key order)
whereas the default Java PriorityQueue is a Min Heap (yields items in ascending key order). Tips: A Max
Heap containing numbers can easily be converted into a Min Heap (and vice versa) by inserting the negated
keys. This is because negating a set of numbers will reverse their order of appearance when sorted. This
trick is used several times in this book. However, if the priority queue is used to store 32-bit signed integers,
an overflow will occur if −231 is negated as 231 − 1 is the maximum value of a 32-bit signed integer.
16
The default partial sort produces the smallest k elements in ascending order.
17
You may have noticed that the time complexity O(n log k) where k is the output size and n is the input
size. This means that the algorithm is ‘output-sensitive’ since its running time depends not only on the
input size but also on the amount of items that it has to output.
18
Note that C++11 is a new C++ standard, older compilers may not support it yet.

45
2.3. NON-LINEAR DS WITH BUILT-IN LIBRARIES 
c Steven & Felix

Exercise 2.3.1: Someone suggested that it is possible to store the key → value pairs
in a sorted array of structs so that we can use the O(log n) binary search for the
example problem above. Is this approach feasible? If no, what is the issue?

Exercise 2.3.2: We will not discuss the basics of BST operations in this book. Instead,
we will use a series of sub-tasks to verify your understanding of BST-related concepts.
We will use Figure 2.2 as an initial reference in all sub-tasks except sub-task 2.

1. Display the steps taken by search(71), search(7), and then search(22).


2. Starting with an empty BST, display the steps taken by insert(15), insert(23),
insert(6), insert(71), insert(50), insert(4), insert(7), and insert(5).
3. Display the steps taken by findMin() (and findMax()).
4. Indicate the inorder traversal of this BST. Is the output sorted?
5. Display the steps taken by successor(23), successor(7), and successor(71).
6. Display the steps taken by delete(5) (a leaf), delete(71) (an internal node
with one child), and then delete(15) (an internal node with two children).

Exercise 2.3.3*: Suppose you are given a reference to the root R of a binary tree
T containing n vertices. You can access a node’s left, right and parent vertices as
well as its key through its reference. Solve each of the following tasks below with the
best possible algorithms that you can think of and analyze their time complexities.
Let’s assume the following constraints: 1 ≤ n ≤ 100K so that O(n2 ) solutions are
theoretically infeasible in a contest environment.

1. Check if T is a BST.
2*. Output the elements in T that are within a given range [a..b] in ascending order.
3*. Output the contents of the leaves of T in descending order.

Exercise 2.3.4*: The inorder traversal (also see Section 4.7.2) of a standard (not
necessarily balanced) BST is known to produce the BST’s element in sorted order and
runs in O(n). Does the code below also produce the BST elements in sorted order?
Can it be made to run in a total time of O(n) instead of O(log n + (n − 1) × log n) =
O(n log n)? If possible, how?

x = findMin(); output x
for (i = 1; i < n; i++) // is this loop O(n log n)?
x = successor(x); output x

Exercise 2.3.5*: Some (hard) problems require us to write our own balanced Bi-
nary Search Tree (BST) implementations due to the need to augment the BST with
additional data (see Chapter 14 of [7]). Challenge: Solve UVa 11849 - CD which is a
pure balanced BST problem with your own balanced BST implementation to test its
performance and correctness.

Exercise 2.3.6: We will not discuss the basics of Heap operations in this book.
Instead, we will use a series of questions to verify your understanding of Heap concepts.

46
CHAPTER 2. DATA STRUCTURES AND LIBRARIES 
c Steven & Felix

1. With Figure 2.3 as the initial heap, display the steps taken by Insert(26).
2. After answering question 1 above, display the steps taken by ExtractMax().
Exercise 2.3.7: Is the structure represented by a 1-based compact array (ignoring
index 0) sorted in descending order a Max Heap?
Exercise 2.3.8*: Prove or disprove this statement: “The second largest element in
a Max Heap with n ≥ 3 distinct elements is always one of the direct children of the
root”. Follow up question: What about the third largest element? Where is/are the
potential location(s) of the third largest element in a Max Heap?
Exercise 2.3.9*: Given a 1-based compact array A containing n integers (1 ≤ n ≤
100K) that are guaranteed to satisfy the Max Heap property, output the elements in
A that are greater than an integer v. What is the best algorithm?
Exercise 2.3.10*: Given an unsorted array S of n distinct integers (2k ≤ n ≤ 100000),
find the largest and smallest k (1 ≤ k ≤ 32) integers in S in O(n log k). Note: For this
written exercise, assume that an O(n log n) algorithm is not acceptable.
Exercise 2.3.11*: One heap operation not directly supported by the C++ STL
priority queue (and Java PriorityQueue) is the UpdateKey(index, newKey) oper-
ation, which allows the (Max) Heap element at a certain index to be updated (increased
or decreased). Write your own binary (Max) Heap implementation with this operation.
Exercise 2.3.12*: Another heap operation that may be useful is the DeleteKey(index)
operation to delete (Max) Heap elements at a certain index. Implement this!
Exercise 2.3.13*: Suppose that we only need the DecreaseKey(index, newKey)
operation, i.e. an UpdateKey operation where the update always makes newKey smaller
than its previous value. Can we use a simpler approach than in Exercise 2.3.11? Hint:
Use lazy deletion, we will use this technique in our Dijkstra code in Section 4.4.3.
Exercise 2.3.14*: Is it possible to use a balanced BST (e.g. C++ STL set or Java
TreeSet) to implement a Priority Queue with the same O(log n) enqueue and dequeue
performance? If yes, how? Are there any potential drawbacks? If no, why?
Exercise 2.3.15*: Is there a better way to implement a Priority Queue if the keys are
all integers within a small range, e.g. [0 . . . 100]? We are expecting an O(1) enqueue
and dequeue performance. If yes, how? If no, why?
Exercise 2.3.16: Which non-linear data structure should you use if you have to
support the following three dynamic operations: 1) many insertions, 2) many deletions,
and 3) many requests for the data in sorted order?
Exercise 2.3.17: There are M strings. N of them are unique (N ≤ M). Which non-
linear data structure discussed in this section should you use if you have to index (label)
these M strings with integers from [0..N-1]? The indexing criteria is as follows: The
first string must be given an index of 0; The next different string must be given index
1, and so on. However, if a string is re-encountered, it must be given the same index
as its earlier copy! One application of this task is in constructing the connection graph
from a list of city names (which are not integer indices!) and a list of highways between
these cities (see Section 2.4.1). To do this, we first have to map these city names into
integer indices (which are far more efficient to work with).

47
2.3. NON-LINEAR DS WITH BUILT-IN LIBRARIES 
c Steven & Felix

Programming exercises solvable with library of non-linear data structures:

• C++ STL map (and Java TreeMap)


1. UVa 00417 - Word Index (generate all words, add to map for auto sorting)
2. UVa 00484 - The Department of ... (maintain frequency with map)
3. UVa 00860 - Entropy Text Analyzer (frequency counting)
4. UVa 00939 - Genes (map child name to his/her gene and parents’ names)
5. UVa 10132 - File Fragmentation (N = number of fragments, B = total bits of
all fragments divided by N/2; try all 2 × N 2 concatenations of two fragments
that have length B; report the one with highest frequency; use map)
6. UVa 10138 - CDVII (map plates to bills, entrace time and position)
7. UVa 10226 - Hardwood Species * (use hashing for a better performance)
8. UVa 10282 - Babelfish (a pure dictionary problem; use map)
9. UVa 10295 - Hay Points (use map to deal with Hay Points dictionary)
10. UVa 10686 - SQF Problem (use map to manage the data)
11. UVa 11239 - Open Source (use map and set to check previous strings)
12. UVa 11286 - Conformity * (use map to keep track of the frequencies)
13. UVa 11308 - Bankrupt Baker (use map and set to help manage the data)
14. UVa 11348 - Exhibition (use map and set to check uniqueness)
15. UVa 11572 - Unique Snowflakes * (use map to record the occurrence in-
dex of a certain snowflake size; use this to determine the answer in O(n log n))
16. UVa 11629 - Ballot evaluation (use map)
17. UVa 11860 - Document Analyzer (use set and map, linear scan)
18. UVa 11917 - Do Your Own Homework (use map)
19. UVa 12504 - Updating a Dictionary (use map; string to string; a bit tedious)
20. UVa 12592 - Slogan Learning of Princess (use map; string to string)
Also check frequency counting section in Section 6.3.
• C++ STL set (Java TreeSet)
1. UVa 00501 - Black Box (use multiset with efficient iterator manipulation)
2. UVa 00978 - Lemmings Battle * (simulation, use multiset)
3. UVa 10815 - Andy’s First Dictionary (use set and string)
4. UVa 11062 - Andy’s Second Dictionary (similar to UVa 10815, with twists)
5. UVa 11136 - Hoax or what * (use multiset)
6. UVa 11849 - CD * (use set to pass the time limit, better: use hashing!)
7. UVa 12049 - Just Prune The List (multiset manipulation)
• C++ STL priority queue (Java PriorityQueue)
1. UVa 01203 - Argus * (LA 3135, Beijing04; use priority queue)
2. UVa 10954 - Add All * (use priority queue, greedy)
3. UVa 11995 - I Can Guess ... * (stack, queue, and priority queue)
Also see the usage of priority queue for topological sorts (see Section 4.2.1),
Kruskal’s19 (see Section 4.3.2), Prim’s (see Section 4.3.3), Dijkstra’s (see
Section 4.4.3), and the A* Search algorithms (see Section 8.2.5)

19
This is another way to implement the edge sorting in Kruskal’s algorithm. Our (C++) implementation
shown in Section 4.3.2 simply uses vector + sort instead of priority queue (a heap sort).

48
CHAPTER 2. DATA STRUCTURES AND LIBRARIES 
c Steven & Felix

2.4 Data Structures with Our Own Libraries


As of 24 May 2013, important data structures shown in this section do not have built-in
support yet in C++ STL or Java API. Thus, to be competitive, contestants should prepare
bug-free implementations of these data structures. In this section, we discuss the key ideas
and example implementations (see the given source code too) of these data structures.

2.4.1 Graph
The graph is a pervasive structure which appears in many Computer Science problems. A
graph (G = (V, E)) in its basic form is simply a set of vertices (V ) and edges (E; storing
connectivity information between vertices in V ). Later in Chapter 3, 4, 8, and 9, we will
explore many important graph problems and algorithms. To prepare ourselves, we will
discuss three basic ways (there are a few other rare structures) to represent a graph G with
V vertices and E edges in this subsection20 .

Figure 2.4: Graph Data Structure Visualization

A). The Adjacency Matrix, usually in the form of a 2D array (see Figure 2.4).
In (programming contest) problems involving graphs, the number of vertices V is usually
known. Thus we can build a ‘connectivity table’ by creating a static 2D array: int
AdjMat[V ][V ]. This has an O(V 2 ) space 21 complexity. For an unweighted graph, set
AdjMat[i][j] to a non-zero value (usually 1) if there is an edge between vertex i-j or
zero otherwise. For a weighted graph, set AdjMat[i][j] = weight(i,j) if there is an
edge between vertex i-j with weight(i,j) or zero otherwise. Adjacency Matrix cannot
be used to store multigraph. For a simple graph without self-loops, the main diagonal
of the matrix contains only zeroes, i.e. AdjMat[i][i] = 0, ∀i ∈ [0..V-1].
An Adjacency Matrix is a good choice if the connectivity between two vertices in a
small dense graph is frequently required. However, it is not recommended for large
sparse graphs as it would require too much space (O(V 2 )) and there would be many
blank (zero) cells in the 2D array. In a competitive setting, it is usually infeasible to
use Adjacency Matrices when the given V is larger than ≈ 1000. Another drawback
of Adjacency Matrix is that it also takes O(V ) time to enumerate the list of neighbors
of a vertex v—an operation common to many graph algorithms—even if a vertex only
has a handful of neighbors. A more compact and efficient graph representation is the
Adjacency List discussed below.
20
The most appropriate notation for the cardinality of a set S is |S|. However, in this book, we will often
overload the meaning of V or E to also mean |V | or |E|, depending on the context.
21
We differentiate between the space and time complexities of data structures. The space complexity is
an asymptotic measure of the memory requirements of a data structure whereas the time complexity is an
asymptotic measure of the time taken to run a certain algorithm or an operation on the data structure.

49
2.4. DATA STRUCTURES WITH OUR OWN LIBRARIES 
c Steven & Felix

B). The Adjacency List, usually in the form of a vector of vector of pairs (see Figure 2.4).
Using the C++ STL: vector<vii> AdjList, with vii defined as in:
typedef pair<int, int> ii; typedef vector<ii> vii; // data type shortcuts
Using the Java API: Vector< Vector < IntegerPair > > AdjList.
IntegerPair is a simple Java class that contains a pair of integers like ii above.
In Adjacency Lists, we have a vector of vector of pairs, storing the list of neighbors
of each vertex u as ‘edge information’ pairs. Each pair contains two pieces of informa-
tion, the index of the neighbouring vertex and the weight of the edge. If the graph is
unweighted, simply store the weight as 0, 1, or drop the weight attribute22 entirely. The
space complexity of Adjacency List is O(V + E) because if there are E bidirectional
edges in a (simple) graph, this Adjacency List will only store 2E ‘edge information’
pairs. As E is usually much smaller than V × (V − 1)/2 = O(V 2 )—the maximum num-
ber of edges in a complete (simple) graph, Adjacency Lists are often more space-efficient
than Adjacency Matrices. Note that Adjacency List can be used to store multigraph.
With Adjacency Lists, we can also enumerate the list of neighbors of a vertex v efficiently.
If v has k neighbors, the enumeration will require O(k) time. Since this is one of the
most common operations in most graph algorithms, it is advisable to use Adjacency
Lists as your first choice of graph representation. Unless otherwise stated, most graph
algorithms discussed in this book use the Adjacency List.

C). The Edge List, usually in the form of a vector of triples (see Figure 2.4).
Using the C++ STL: vector< pair<int, ii> > EdgeList.
Using the Java API: Vector< IntegerTriple > EdgeList.
IntegerTriple is a class that contains a triple of integers like pair<int, ii> above.
In the Edge List, we store a list of all E edges, usually in some sorted order. For
directed graphs, we can store a bidirectional edge twice, one for each direction. The
space complexity is clearly O(E). This graph representation is very useful for Kruskal’s
algorithm for MST (Section 4.3.2), where the collection of undirected edges need to
be sorted23 by ascending weight. However, storing graph information in Edge List
complicates many graph algorithms that require the enumeration of edges incident to a
vertex.

Visualization: www.comp.nus.edu.sg/∼stevenha/visualization/graphds.html
Source code: ch2 07 graph ds.cpp/java

Implicit Graph
Some graphs do not have to be stored in a graph data structure or explicitly generated for
the graph to be traversed or operated upon. Such graphs are called implicit graphs. You
will encounter them in the subsequent chapters. Implicit graphs can come in two flavours:
1. The edges can be determined easily.
Example 1: Navigating a 2D grid map (see Figure 2.5.A). The vertices are the cells in
the 2D character grid where ‘.’ represents land and ‘#’ represents an obstacle. The
edges can be determined easily: There is an edge between two neighboring cells in the
22
For simplicity, we will always assume that the second attribute exists in all graph implementations in
this book although it is not always used.
23
pair objects in C++ can be easily sorted. The default sorting criteria is to sort on the first item and
then the second item for tie-breaking. In Java, we can write our own IntegerPair/IntegerTriple class
that implements Comparable.

50
CHAPTER 2. DATA STRUCTURES AND LIBRARIES 
c Steven & Felix

grid if they share an N/S/E/W border and if both are ‘.’ (see Figure 2.5.B).
Example 2: The graph of chess knight movements on an 8 × 8 chessboard. The vertices
are the cells in the chessboard. Two squares in the chessboard have an edge between
them if they differ by two squares horizontally and one square vertically (or two squares
vertically and one square horizontally). The first three rows and four columns of a
chessboard are shown in Figure 2.5.C (many other vertices and edges are not shown).
2. The edges can be determined with some rules.
Example: A graph contains N vertices ([1..N]). There is an edge between two vertices
i and j if (i + j) is a prime. See Figure 2.5.D that shows such a graph with N = 5 and
several more examples in Section 8.2.3.

Figure 2.5: Implicit Graph Examples

Exercise 2.4.1.1*: Create the Adjacency Matrix, Adjacency List, and Edge List represen-
tations of the graphs shown in Figure 4.1 (Section 4.2.1) and in Figure 4.9 (Section 4.2.9).
Hint: Use the graph data structure visualization tool shown above.
Exercise 2.4.1.2*: Given a (simple) graph in one representation (Adjacency Matrix/AM,
Adjacency List/AL, or Edge List/EL), convert it into another graph representation in the
most efficient way possible! There are 6 possible conversions here: AM to AL, AM to EL,
AL to AM, AL to EL, EL to AM, and EL to AL.
Exercise 2.4.1.3: If the Adjacency Matrix of a (simple) graph has the property that it is
equal to its transpose, what does this imply?
Exercise 2.4.1.4*: Given a (simple) graph represented by an Adjacency Matrix, perform
the following tasks in the most efficient manner. Once you have figured out how to do this
for Adjacency Matrices, perform the same task with Adjacency Lists and then Edge Lists.
1. Count the number of vertices V and directed edges E (assume that a bidirectional
edge is equivalent to two directed edges) of the graph.
2*. Count the in-degree and the out-degree of a certain vertex v.
3*. Transpose the graph (reverse the direction of each edges).
4*. Check if the graph is a complete graph Kn . Note: A complete graph is a simple
undirected graph in which every pair of distinct vertices is connected by a single edge.
5*. Check if the graph is a tree (a connected undirected graph with E = V − 1 edges).
6*. Check if the graph is a star graph Sk . Note: A star graph Sk is a complete bipartite
K1,k graph. It is a tree with only one internal vertex and k leaves.
Exercise 2.4.1.5*: Research other possible methods of representing graphs other than the
ones discussed above, especially for storing special graphs!

51
2.4. DATA STRUCTURES WITH OUR OWN LIBRARIES 
c Steven & Felix

2.4.2 Union-Find Disjoint Sets


The Union-Find Disjoint Set (UFDS) is a data structure to model a collection of disjoint sets
with the ability to efficiently24 —in ≈ O(1)—determine which set an item belongs to (or to
test whether two items belong to the same set) and to unite two disjoint sets into one larger
set. Such data structure can be used to solve the problem of finding connected components
in an undirected graph (Section 4.2.3). Initialize each vertex to a separate disjoint set, then
enumerate the graph’s edges and join every two vertices/disjoint sets connected by an edge.
We can then test if two vertices belong to the same component/set easily.
These seemingly simple operations are not efficiently supported by the C++ STL set
(and Java TreeSet), which is not designed for this purpose. Having a vector of sets and
looping through each one to find which set an item belongs to is expensive! C++ STL
set union (in algorithm) will not be efficient enough although it combines two sets in
linear time as we still have to deal with shuffling the contents of the vector of sets! To
support these set operations efficiently, we need a better data structure—the UFDS.
The main innovation of this data structure is in choosing a representative ‘parent’ item
to represent a set. If we can ensure that each set is represented by only one unique item,
then determining if items belong to the same set becomes far simpler: The representative
‘parent’ item can be used as a sort of identifier for the set. To achieve this, the Union-Find
Disjoint Set creates a tree structure where the disjoint sets form a forest of trees. Each tree
corresponds to a disjoint set. The root of the tree is determined to be the representative
item for a set. Thus, the representative set identifier for an item can be obtained simply
by following the chain of parents to the root of the tree, and since a tree can only have one
root, this representative item can be used as a unique identifier for the set.
To do this efficiently, we store the index of the parent item and (the upper bound of)
the height of the tree of each set (vi p and vi rank in our implementation). Remember
that vi is our shortcut for a vector of integers. p[i] stores the immediate parent of item i.
If item i is the representative item of a certain disjoint set, then p[i] = i, i.e. a self-loop.
rank[i] yields (the upper bound of) the height of the tree rooted at item i.
In this section, we will use 5 disjoint sets {0, 1, 2, 3, 4} to illustrate the usage of this
data structure. We initialize the data structure such that each item is a disjoint set by itself
with rank 0 and the parent of each item is initially set to itself.
To unite two disjoint sets, we set the representative item (root) of one disjoint set to be
the new parent of the representative item of the other disjoint set. This effectively merges
the two trees in the Union-Find Disjoint Set representation. As such, unionSet(i, j) will
cause both items ‘i’ and ‘j’ to have the same representative item—directly or indirectly. For
efficiency, we can use the information contained in vi rank to set the representative item
of the disjoint set with higher rank to be the new parent of the disjoint set with lower rank,
thereby minimizing the rank of the resulting tree. If both ranks are the same, we arbitrarily
choose one of them as the new parent and increase the resultant root’s rank. This is the
‘union by rank’ heuristic. In Figure 2.6, top, unionSet(0, 1) sets p[0] to 1 and rank[1]
to 1. In Figure 2.6, middle, unionSet(2, 3) sets p[2] to 3 and rank[3] to 1.
For now, let’s assume that function findSet(i) simply calls findSet(p[i]) recursively
to find the representative item of a set, returning findSet(p[i]) whenever p[i] != i and i
otherwise. In Figure 2.6, bottom, when we call unionSet(4, 3), we have rank[findSet(4)]
= rank[4] = 0 which is smaller than rank[findSet(3)] = rank[3] = 1, so we set p[4]
= 3 without changing the height of the resulting tree—this is the ‘union by rank’ heuristic
24
M operations of this UFDS data structure with ‘path compression’ and ‘union by rank’ heuristics run
in O(M × α(n)). However, since the inverse Ackermann function α(n) grows very slowly, i.e. its value is just
less than 5 for practical input size n ≤ 1M in programming contest setting, we can treat α(n) as constant.

52
CHAPTER 2. DATA STRUCTURES AND LIBRARIES 
c Steven & Felix

Figure 2.6: unionSet(0, 1) → (2, 3) → (4, 3) and isSameSet(0, 4)

at work. With the heuristic, the path taken from any node to the representative item by
following the chain of ‘parent’ links is effectively minimized.
In Figure 2.6, bottom, isSameSet(0, 4) demonstrates another operation for this data
structure. This function isSameSet(i, j) simply calls findSet(i) and findSet(j) and
checks if both refer to the same representative item. If they do, then ‘i’ and ‘j’ both belong
to the same set. Here, we see that findSet(0) = findSet(p[0]) = findSet(1) = 1 is
not the same as findSet(4)= findSet(p[4]) = findSet(3) = 3. Therefore item 0 and
item 4 belongs to different disjoint sets.

Figure 2.7: unionSet(0, 3) → findSet(0)

There is a technique that can vastly speed up the findSet(i) function: Path compression.
Whenever we find the representative (root) item of a disjoint set by following the chain of
‘parent’ links from a given item, we can set the parent of all items traversed to point directly
to the root. Any subsequent calls to findSet(i) on the affected items will then result in
only one link being traversed. This changes the structure of the tree (to make findSet(i)
more efficient) but yet preserves the actual constitution of the disjoint set. Since this will
occur any time findSet(i) is called, the combined effect is to render the runtime of the
findSet(i) operation to run in an extremely efficient amortized O(M × α(n)) time.
In Figure 2.7, we demonstrate this ‘path compression’. First, we call unionSet(0, 3).
This time, we set p[1] = 3 and update rank[3] = 2. Now notice that p[0] is unchanged,
i.e. p[0] = 1. This is an indirect reference to the (true) representative item of the set, i.e.
p[0] = 1 → p[1] = 3. Function findSet(i) will actually require more than one step to

53
2.4. DATA STRUCTURES WITH OUR OWN LIBRARIES 
c Steven & Felix

traverse the chain of ‘parent’ links to the root. However, once it finds the representative
item, (e.g. ‘x’) for that set, it will compress the path by setting p[i] = x, i.e. findSet(0)
sets p[0] = 3. Therefore, subsequent calls of findSet(i) will be just O(1). This simple
strategy is aptly named the ‘path compression’ heuristic. Note that rank[3] = 2 now no
longer reflects the true height of the tree. This is why rank only reflects the upper bound of
the actual height of the tree. Our C++ implementation is shown below:

class UnionFind { // OOP style


private: vi p, rank; // remember: vi is vector<int>
public:
UnionFind(int N) { rank.assign(N, 0);
p.assign(N, 0); for (int i = 0; i < N; i++) p[i] = i; }
int findSet(int i) { return (p[i] == i) ? i : (p[i] = findSet(p[i])); }
bool isSameSet(int i, int j) { return findSet(i) == findSet(j); }
void unionSet(int i, int j) {
if (!isSameSet(i, j)) { // if from different set
int x = findSet(i), y = findSet(j);
if (rank[x] > rank[y]) p[y] = x; // rank keeps the tree short
else { p[x] = y;
if (rank[x] == rank[y]) rank[y]++; }
} } };

Visualization: www.comp.nus.edu.sg/∼stevenha/visualization/ufds.html
Source code: ch2 08 unionfind ds.cpp/java

Exercise 2.4.2.1: There are two more queries that are commonly performed in this data
structure. Update the code provided in this section to support these two queries efficiently:
int numDisjointSets() that returns the number of disjoint sets currently in the structure
and int sizeOfSet(int i) that returns the size of set that currently contains item i.
Exercise 2.4.2.2*: Given 8 disjoint sets: {0, 1, 2, . . . , 7}, please create a sequence of
unionSet(i, j) operations to create a tree with rank = 3! Is this possible for rank = 4?

Profiles of Data Structure Inventors


George Boole (1815-1864) was an English mathematician, philosopher, and logician. He is
best known to Computer Scientists as the founder of Boolean logic, the foundation of modern
digital computers. Boole is regarded as the founder of the field of Computer Science.
Rudolf Bayer (born 1939) has been Professor (emeritus) of Informatics at the Technical
University of Munich. He invented the Red-Black (RB) tree used in the C++ STL map/set.
Georgii Adelson-Velskii (born 1922) is a Soviet mathematician and computer scientist.
Along with Evgenii Mikhailovich Landis, he invented the AVL tree in 1962.
Evgenii Mikhailovich Landis (1921-1997) was a Soviet mathematician. The name of the
AVL tree is an abbreviation of the two inventors: Adelson-Velskii and Landis himself.

54
CHAPTER 2. DATA STRUCTURES AND LIBRARIES 
c Steven & Felix

2.4.3 Segment Tree


In this subsection, we will discuss a data structure which can efficiently answer dynamic 25
range queries. One such range query is the problem of finding the index of the minimum
element in an array within range [i..j]. This is more commonly known as the Range
Minimum Query (RMQ) problem. For example, given an array A of size n = 7 below,
RMQ(1, 3) = 2, as the index 2 contains the minimum element among A[1], A[2], and
A[3]. To check your understanding of RMQ, verify that in the array A below, RMQ(3, 4)
= 4, RMQ(0, 0) = 0, RMQ(0, 1) = 1, and RMQ(0, 6) = 5. For the next few paragraphs,
assume that array A is the same.
Array Values 18 17 13 19 15 11 20
A Indices 0 1 2 3 4 5 6
There are several ways to implement the RMQ. One trivial algorithm is to simply iterate
the array from index i to j and report the index with the minimum value, but this will run
in O(n) time per query. When n is large and there are many queries, such an algorithm may
be infeasible.
In this section, we answer the dynamic RMQ problem with a Segment Tree, which is
another way to arrange data in a binary tree. There are several ways to implement the
Segment Tree. Our implementation uses the same concept as the 1-based compact array in
the binary heap where we use vi (our shortcut for vector<int>) st to represent the binary
tree. Index 1 (skipping index 0) is the root and the left and right children of index p are
index 2 × p and (2 × p) + 1 respectively (also see Binary Heap discussion in Section 2.3).
The value of st[p] is the RMQ value of the segment associated with index p.
The root of segment tree represents segment [0, n-1]. For each segment [L, R] stored
in index p where L != R, the segment will be split into [L, (L+R)/2] and [(L+R)/2+1, R]
in a left and right vertices. The left sub-segment and right sub-segment will be stored in index
2×p and (2×p)+1 respectively. When L = R, it is clear that st[p] = L (or R). Otherwise, we
will recursively build the segment tree, comparing the minimum value of the left and the right
sub-segments and updating the st[p] of the segment. This process is implemented in the
build routine below. This build routine creates up to O(1 + 2 + 4 + 8 + . . .+ 2log2 n ) = O(2n)
(smaller) segments and therefore runs in O(n). However, as we use simple 1-based compact
array indexing, we need st to be at least of size 2 ∗ 2(log2 (n)+1 . In our implementation, we
simply use a loose upper bound of space complexity O(4n) = O(n). For array A above, the
corresponding segment tree is shown in Figure 2.8 and 2.9.
With the segment tree ready, answering an RMQ can be done in O(log n). The answer
for RMQ(i, i) is trivial—simply return i itself. However, for the general case RMQ(i, j),
further checks are needed. Let p1 = RMQ(i, (i+j)/2) and p2 = RMQ((i+j)/2 + 1, j).
Then RMQ(i, j) is p1 if A[p1] ≤ A[p2] or p2 otherwise. This process is implemented in
the rmq routine below.
Take for example the query RMQ(1, 3). The process in Figure 2.8 is as follows: Start from
the root (index 1) which represents segment [0, 6]. We cannot use the stored minimum
value of segment [0, 6] = st[1] = 5 as the answer for RMQ(1, 3) since it is the minimum
value over a larger26 segment than the desired [1, 3]. From the root, we only have to go to
the left subtree as the root of the right subtree represents segment [4, 6] which is outside27
the desired range in RMQ(1, 3).
25
For dynamic problems, we need to frequently update and query the data. This makes pre-processing
techniques useless.
26
Segment [L, R] is said to be larger than query range [i, j] if [L, R] is not outside the query range
and not inside query range (see the other footnotes).
27
Segment [L, R] is said to be outside query range [i, j] if i > R || j < L.

55
2.4. DATA STRUCTURES WITH OUR OWN LIBRARIES 
c Steven & Felix

Figure 2.8: Segment Tree of Array A = {18, 17, 13, 19, 15, 11, 20} and RMQ(1, 3)

We are now at the root of the left subtree (index 2) that represents segment [0, 3]. This
segment [0, 3] is still larger than the desired RMQ(1, 3). In fact, RMQ(1, 3) intersects
both the left sub-segment [0, 1] (index 4) and the right sub-segment [2, 3] (index 5) of
segment [0, 3], so we have to explore both subtrees (sub-segments).
The left segment [0, 1] (index 4) of [0, 3] (index 2) is not yet inside the RMQ(1, 3),
so another split is necessary. From segment [0, 1] (index 4), we move right to segment
[1, 1] (index 9), which is now inside28 [1, 3]. At this point, we know that RMQ(1, 1) =
st[9] = 1 and we can return this value to the caller. The right segment [2, 3] (index 5)
of [0, 3] (index 2) is inside the required [1, 3]. From the stored value inside this vertex,
we know that RMQ(2, 3) = st[5] = 2. We do not need to traverse further down.
Now, back in the call to segment [0, 3] (index 2), we now have p1 = RMQ(1, 1) = 1
and p2 = RMQ(2, 3) = 2. Because A[p1] > A[p2] since A[1] = 17 and A[2] = 13, we
now have RMQ(1, 3) = p2 = 2. This is the final answer.

Figure 2.9: Segment Tree of Array A = {18, 17, 13, 19, 15, 11, 20} and RMQ(4, 6)

Now let’s take a look at another example: RMQ(4, 6). The execution in Figure 2.9 is as
follows: We again start from the root segment [0, 6] (index 1). Since it is larger than
the RMQ(4, 6), we move right to segment [4, 6] (index 3) as segment [0, 3] (index 2)
is outside. Since this segment exactly represents RMQ(4, 6), we simply return the index of
minimum element that is stored in this vertex, which is 5. Thus RMQ(4, 6) = st[3] = 5.
This data structure allows us to avoid traversing the unnecessary parts of the tree! In the
worst case, we have two root-to-leaf paths which is just O(2 ×log(2n)) = O(log n). Example:
In RMQ(3, 4) = 4, we have one root-to-leaf path from [0, 6] to [3, 3] (index 1 → 2 →
5 → 11) and another root-to-leaf path from [0, 6] to [4, 4] (index 1 → 3 → 6 → 12).
If the array A is static (i.e. unchanged after it is instantiated), then using a Segment
Tree to solve the RMQ problem is overkill as there exists a Dynamic Programming (DP)
solution that requires O(n log n) one-time pre-processing and allows for O(1) per RMQ. This
DP solution will be discussed later in Section 9.33.
Segment Tree is useful if the underlying array is frequently updated (dynamic). For
example, if A[5] is now changed from 11 to 99, then we just need to update the vertices
along the leaf to root path in O(log n). See path: [5, 5] (index 13, st[13] is unchanged)
→ [4, 5] (index 6, st[6] = 4 now) → [4, 6] (index 3, st[3] = 4 now) → [0, 6] (index
28
Segment [L, R] is said to be inside query range [i, j] if L ≥ i && R ≤ j.

56
CHAPTER 2. DATA STRUCTURES AND LIBRARIES 
c Steven & Felix

1, st[1] = 2 now) in Figure 2.10. For comparison, the DP solution presented in Section
9.33 requires another O(n log n) pre-processing to update the structure and is ineffective for
such dynamic updates.

Figure 2.10: Updating Array A to {18, 17, 13, 19, 15, 99, 20}

Our Segment Tree implementation is shown below. The code shown here supports only static
RMQs (dynamic updates are left as an exercise to the reader).

class SegmentTree { // the segment tree is stored like a heap array


private: vi st, A; // recall that vi is: typedef vector<int> vi;
int n;
int left (int p) { return p << 1; } // same as binary heap operations
int right(int p) { return (p << 1) + 1; }

void build(int p, int L, int R) { // O(n)


if (L == R) // as L == R, either one is fine
st[p] = L; // store the index
else { // recursively compute the values
build(left(p) , L , (L + R) / 2);
build(right(p), (L + R) / 2 + 1, R );
int p1 = st[left(p)], p2 = st[right(p)];
st[p] = (A[p1] <= A[p2]) ? p1 : p2;
} }

int rmq(int p, int L, int R, int i, int j) { // O(log n)


if (i > R || j < L) return -1; // current segment outside query range
if (L >= i && R <= j) return st[p]; // inside query range

// compute the min position in the left and right part of the interval
int p1 = rmq(left(p) , L , (L+R) / 2, i, j);
int p2 = rmq(right(p), (L+R) / 2 + 1, R , i, j);

if (p1 == -1) return p2; // if we try to access segment outside query


if (p2 == -1) return p1; // same as above
return (A[p1] <= A[p2]) ? p1 : p2; // as in build routine
}

public:
SegmentTree(const vi &_A) {
A = _A; n = (int)A.size(); // copy content for local usage
st.assign(4 * n, 0); // create large enough vector of zeroes
build(1, 0, n - 1); // recursive build
}

57
2.4. DATA STRUCTURES WITH OUR OWN LIBRARIES 
c Steven & Felix

int rmq(int i, int j) { return rmq(1, 0, n - 1, i, j); } // overloading


};

int main() {
int arr[] = { 18, 17, 13, 19, 15, 11, 20 }; // the original array
vi A(arr, arr + 7);
SegmentTree st(A);
printf("RMQ(1, 3) = %d\n", st.rmq(1, 3)); // answer = index 2
printf("RMQ(4, 6) = %d\n", st.rmq(4, 6)); // answer = index 5
} // return 0;

Visualization: www.comp.nus.edu.sg/∼stevenha/visualization/segmenttree.html
Source code: ch2 09 segmenttree ds.cpp/java

Exercise 2.4.3.1*: Draw the Segment Tree corresponding to array A = {10, 2, 47, 3,
7, 9, 1, 98, 21}. Answer RMQ(1, 7) and RMQ(3, 8)! Hint: Use the Segment Tree
visualization tool shown above.
Exercise 2.4.3.2*: In this section, we have seen how Segment Trees can be used to answer
Range Minimum Queries (RMQs). Segment Trees can also be used to answer dynamic
Range Sum Queries (RSQ(i, j)), i.e. a sum from A[i] + A[i + 1] + ...+ A[j]. Modify
the given Segment Tree code above to deal with RSQ.
Exercise 2.4.3.3: Using a similar Segment Tree to Exercise 2.4.3.1 above, answer the
queries RSQ(1, 7) and RSQ(3, 8). Is this a good approach to solve the problem if array A
is never changed? (also see Section 3.5.2).
Exercise 2.4.3.4*: The Segment Tree code shown above lacks the (point) update operation
as discussed in the body text. Add the O(log n) update function to update the value of a
certain index (point) in array A and simultaneously update the corresponding Segment Tree!
Exercise 2.4.3.5*: The (point) update operation shown in the body text only changes the
value of a certain index in array A. What if we delete existing elements of array A or insert a
new elements into array A? Can you explain what will happen with the given Segment Tree
code and what you should do to address it?
Exercise 2.4.3.6*: There is also one more important Segment Tree operation that has not
yet been discussed, the range update operation. Suppose a certain subarray of A is updated
to a certain common value. Can we update the Segment Tree efficiently? Study and solve
UVa 11402 - Ahoy Pirates—a problem that requires range updates.

58
CHAPTER 2. DATA STRUCTURES AND LIBRARIES 
c Steven & Felix

2.4.4 Binary Indexed (Fenwick) Tree


Fenwick Tree—also known as Binary Indexed Tree (BIT)—were invented by Peter M.
Fenwick in 1994 [18]. In this book, we will use the term Fenwick Tree as opposed to BIT
in order to differentiate with the standard bit manipulations. The Fenwick Tree is a useful
data structure for implementing dynamic cumulative frequency tables. Suppose we have29
test scores of m = 11 students f = {2,4,5,5,6,6,6,7,7,8,9} where the test scores are
integer values ranging from [1..10]. Table 2.1 shows the frequency of each individual test
score ∈ [1..10] and the cumulative frequency of test scores ranging from [1..i] denoted
by cf[i]—that is, the sum of the frequencies of test scores 1, 2, ..., i.

Index/ Frequency Cumulative Short Comment


Score f Frequency cf
0 - - Index 0 is ignored (as the sentinel value).
1 0 0 cf[1] = f[1] = 0.
2 1 1 cf[2] = f[1] + f[2] = 0 + 1 = 1.
3 0 1 cf[3] = f[1] + f[2] + f[3] = 0 + 1 + 0 = 1.
4 1 2 cf[4] = cf[3] + f[4] = 1 + 1 = 2.
5 2 4 cf[5] = cf[4] + f[5] = 2 + 2 = 4.
6 3 7 cf[6] = cf[5] + f[6] = 4 + 3 = 7.
7 2 9 cf[7] = cf[6] + f[7] = 7 + 2 = 9.
8 1 10 cf[8] = cf[7] + f[8] = 9 + 1 = 10.
9 1 11 cf[9] = cf[8] + f[9] = 10 + 1 = 11.
10 0 11 cf[10] = cf[9] + f[10] = 11 + 0 = 11.

Table 2.1: Example of a Cumulative Frequency Table

The cumulative frequency table can also be used as a solution to the Range Sum Query
(RSQ) problem mentioned in Exercise 2.4.3.2*. It stores RSQ(1, i) ∀i ∈ [1..n] where
n is the largest integer index/score30 . In the example above, we have n = 10, RSQ(1, 1)
= 0, RSQ(1, 2) = 1, . . . , RSQ(1, 6) = 7, . . . , RSQ(1, 8) = 10, . . . , and RSQ(1, 10) =
11. We can then obtain the answer to the RSQ for an arbitrary range RSQ(i, j) when
i = 1 by subtracting RSQ(1, j) - RSQ(1, i - 1). For example, RSQ(4, 6) = RSQ(1, 6)
- RSQ(1, 3) = 7 - 1 = 6.
If the frequencies are static, then the cumulative frequency table as in Table 2.1 can
be computed efficiently with a simple O(n) loop. First, set cf[1] = f[1]. Then, for i ∈
[2..n], compute cf[i] = cf[i - 1] + f[i]. This will be discussed further in Section
3.5.2. However, when the frequencies are frequently updated (increased or decreased) and
the RSQs are frequently asked afterwards, it is better to use a dynamic data structure.
Instead of using a Segment Tree to implement a dynamic cumulative frequency table,
we can implement the far simpler Fenwick Tree instead (compare the source code for both
implementations, provided in this section and in the previous Section 2.4.3). This is perhaps
one of the reasons why the Fenwick Tree is currently included in the IOI syllabus [20]. Fen-
wick Tree operations are also extremely efficient as they use fast bit manipulation techniques
(see Section 2.2).
In this section, we will use the function LSOne(i) (which is actually (i & (-i))) exten-
sively, naming it to match its usage in the original paper [18]. In Section 2.2, we have seen
that the operation (i & (-i)) produces the first Least Significant One-bit in i.
29
The test scores are shown in sorted order for simplicity, they do not have to be sorted.
30
Please differentiate m = the number of data points and n = the largest integer value among the m data
points. The meaning of n in Fenwick Tree is a bit different compared to other data structures in this book.

59
2.4. DATA STRUCTURES WITH OUR OWN LIBRARIES 
c Steven & Felix

The Fenwick Tree is typically implemented as an array (we use a vector for size flexibil-
ity). The Fenwick Tree is a tree that is indexed by the bits of its integer keys. These integer
keys fall within the fixed range [1..n]—skipping31 index 0. In a programming contest envi-
ronment, n can approach ≈ 1M so that the Fenwick Tree covers the range [1..1M]—large
enough for many practical (contest) problems. In Table 2.1 above, the scores [1..10] are
the integer keys in the corresponding array with size n = 10 and m = 11 data points.
Let the name of the Fenwick Tree array be ft. Then, the element at index i is responsible
for elements in the range [i-LSOne(i)+1..i] and ft[i] stores the cumulative frequency
of elements {i-LSOne(i)+1, i-LSOne(i)+2, i-LSOne(i)+3, .., i}. In Figure 2.11, the
value of ft[i] is shown in the circle above index i and the range [i-LSOne(i)+1..i] is
shown as a circle and a bar (if the range spans more than one index) above index i. We can
see that ft[4] = 2 is responsible for range [4-4+1..4] = [1..4], ft[6] = 5 is responsible
for range [6-2+1..6] = [5..6], ft[7] = 2 is responsible for range [7-1+1..7] = [7..7],
ft[8] = 10 is responsible for range [8-8+1..8] = [1..8] etc32 .
With such an arrangement, if we want to obtain the cumulative frequency between
[1..b], i.e. rsq(b), we simply add ft[b], ft[b’], ft[b’’], . . . until index bi is 0. This
sequence of indices is obtained via subtracting the Least Significant One-bit via the bit ma-
nipulation expression: b’ = b - LSOne(b). Iteration of this bit manipulation effectively
strips off the least significant one-bit of b at each step. As an integer b only has O(log b)
bits, rsq(b) runs in O(log n) time when b = n. In Figure 2.11, rsq(6) = ft[6] + ft[4]
= 5 + 2 = 7. Notice that indices 4 and 6 are responsible for range [1..4] and [5..6],
respectively. By combining them, we account for the entire range of [1..6]. The indices
6, 4, and 0 are related in their binary form: b = 610 = (110)2 can be transformed to b’ =
410 = (100)2 and subsequently to b’’ = 010 = (000)2 .

Figure 2.11: Example of rsq(6)

With rsq(b) available, obtaining the cumulative frequency between two indices [a..b]
where a != 1 is simple, just evaluate rsq(a, b) = rsq(b) - rsq(a - 1). For example, if
we want to compute rsq(4, 6), we can simply return rsq(6) - rsq(3) = (5+2) - (0+1)
= 7 - 1 = 6. Again, this operation runs in O(2 × log b) ≈ O(log n) time when b = n.
Figure 2.12 displays the value of rsq(3).
When updating the value of the element at index k by adjusting its value by v (note
that v can be either positive or negative), i.e. calling adjust(k, v), we have to update
ft[k], ft[k’], ft[k’’], . . . until index ki exceeds n. This sequence of indices are obtained
31
We have chosen to follow the original implementation by [18] that ignores index 0 to facilitate an easier
understanding of the bit manipulation operations of Fenwick Tree. Note that index 0 has no bit turned on.
Thus, the operation i +/- LSOne(i) simply returns i when i = 0. Index 0 is also used as the terminating
condition in the rsq function.
32
In this book, we will not detail why this arrangement works and will instead show that it allows for
efficient O(log n) update and RSQ operations. Interested readers are advised to read [18].

60
CHAPTER 2. DATA STRUCTURES AND LIBRARIES 
c Steven & Felix

Figure 2.12: Example of rsq(3)

via this similar iterative bit manipulation expression: k’ = k + LSOne(k). Starting from
any integer k, the operation adjust(k, v) will take at most O(log n) steps until k > n. In
Figure 2.13, adjust(5, 1) will affect (add +1 to) ft[k] at indices k = 510 = (101)2 , k’ =
(101)2 + (001)2 = (110)2 = 610 , and k’’ = (110)2 + (010)2 = (1000)2 = 810 via the
expression given above. Notice that if you project a line upwards from index 5 in Figure
2.13, you will see that the line indeed intersects the ranges under the responsibility of index
5, index 6, and index 8.

Figure 2.13: Example of adjust(5, 1)

In summary, Fenwick Tree supports both RSQ and update operations in just O(n) space and
O(log n) time given a set of m integer keys that ranges from [1..n]. This makes Fenwick
Tree an ideal data structure for solving dynamic RSQ problems on with discrete arrays (the
static RSQ problem can be solved with simple O(n) pre-processing and O(1) per query as
shown earlier). Our short C++ implementation of a basic Fenwick Tree is shown below.

class FenwickTree {
private: vi ft; // recall that vi is: typedef vector<int> vi;
public: FenwickTree(int n) { ft.assign(n + 1, 0); } // init n + 1 zeroes
int rsq(int b) { // returns RSQ(1, b)
int sum = 0; for (; b; b -= LSOne(b)) sum += ft[b];
return sum; } // note: LSOne(S) (S & (-S))
int rsq(int a, int b) { // returns RSQ(a, b)
return rsq(b) - (a == 1 ? 0 : rsq(a - 1)); }
// adjusts value of the k-th element by v (v can be +ve/inc or -ve/dec)
void adjust(int k, int v) { // note: n = ft.size() - 1
for (; k < (int)ft.size(); k += LSOne(k)) ft[k] += v; }
};

61
2.4. DATA STRUCTURES WITH OUR OWN LIBRARIES 
c Steven & Felix

int main() {
int f[] = { 2,4,5,5,6,6,6,7,7,8,9 }; // m = 11 scores
FenwickTree ft(10); // declare a Fenwick Tree for range [1..10]
// insert these scores manually one by one into an empty Fenwick Tree
for (int i = 0; i < 11; i++) ft.adjust(f[i], 1); // this is O(k log n)
printf("%d\n", ft.rsq(1, 1)); // 0 => ft[1] = 0
printf("%d\n", ft.rsq(1, 2)); // 1 => ft[2] = 1
printf("%d\n", ft.rsq(1, 6)); // 7 => ft[6] + ft[4] = 5 + 2 = 7
printf("%d\n", ft.rsq(1, 10)); // 11 => ft[10] + ft[8] = 1 + 10 = 11
printf("%d\n", ft.rsq(3, 6)); // 6 => rsq(1, 6) - rsq(1, 2) = 7 - 1
ft.adjust(5, 2); // update demo
printf("%d\n", ft.rsq(1, 10)); // now 13
} // return 0;

Visualization: www.comp.nus.edu.sg/∼stevenha/visualization/bit.html
Source code: ch2 10 fenwicktree ds.cpp/java

Exercise 2.4.4.1: Just a simple exercise of the two basic bit-manipulation operations used
in the Fenwick Tree: What are the values of 90 - LSOne(90) and 90 + LSOne(90)?
Exercise 2.4.4.2: What if the problem that you want to solve includes an element at integer
key 0? Recall that the standard integer key range in our library code is is [1..n] and that
this implementation cannot use index 0 since it is used as the terminating condition of rsq.
Exercise 2.4.4.3: What if the problem that you want to solve uses non-integer keys? For
example, what if the test scores shown in Table 2.1 above are f = {5.5, 7.5, 8.0, 10.0}
(i.e. allowing either a 0 or a 5 after the decimal place)? What if the test scores are f =
{5.53, 7.57, 8.10, 9.91} (i.e. allowing for two digits after the decimal point)?
Exercise 2.4.4.4: The Fenwick Tree supports an additional operation that we have decided
to leave as an exercise to the reader: Find the smallest index with a given cumulative
frequency. For example, we may need to determine the minimum index/score i in Table 2.1
such that there are at least 7 students covered in the range [1..i] (index/score 6 in this
case). Implement this feature.
Exercise 2.4.4.5*: Solve this dynamic RSQ problem: UVa 12086 - Potentiometers using
both a Segment Tree and Fenwick Tree. Which solution is easier to produce in this case?
Also see Table 2.2 for a comparison between these two data structures.
Exercise 2.4.4.6*: Extend the 1D Fenwick Tree to 2D!
Exercise 2.4.4.7*: Fenwick Trees are normally used for point update and range (sum)
query. Show how to use a Fenwick Tree for range update and point queries. For example,
given lots of intervals with small ranges (from 1 to at most 1 million) determine the number
of intervals encompassing index i.

Profile of Data Structure Inventors


Peter M. Fenwick is a Honorary Associate Professor in the University of Auckland. He
invented the Binary Indexed Tree in 1994 [18] as “cumulative frequency tables of arithmetic
compression”. The BIT has since been included in the IOI syllabus [20] and used in many
contest problems for its efficient yet easy to implement data structure.

62
CHAPTER 2. DATA STRUCTURES AND LIBRARIES 
c Steven & Felix

Feature Segment Tree Fenwick Tree


Build Tree from Array O(n) O(m log n)
Dynamic RMin/MaxQ OK Very limited
Dynamic RSQ OK OK
Query Complexity O(log n) O(log n)
Point Update Complexity O(log n) O(log n)
Length of Code Longer Shorter

Table 2.2: Comparison Between Segment Tree and Fenwick Tree

Programming exercises that use the data structures discussed and implemented:
• Graph Data Structures Problems
1. UVa 00599 - The Forrest for the Trees * (v−e = number of connected
components, keep a bitset of size 26 to count the number of vertices that
have some edge. Note: Also solvable with Union-Find)
2. UVa 10895 - Matrix Transpose * (transpose adjacency list)
3. UVa 10928 - My Dear Neighbours (counting out degrees)
4. UVa 11550 - Demanding Dilemma (graph representation, incidence matrix)
5. UVa 11991 - Easy Problem from ... * (use the idea of an Adj List)
Also see: More graph problems in Chapter 4
• Union-Find Disjoint Sets
1. UVa 00793 - Network Connections * (trivial; application of disjoint sets)
2. UVa 01197 - The Suspects (LA 2817, Kaohsiung03, Connected Components)
3. UVa 10158 - War (advanced usage of disjoint sets with a nice twist; memorize
list of enemies)
4. UVa 10227 - Forests (merge two disjoint sets if they are consistent)
5. UVa 10507 - Waking up brain * (disjoint sets simplifies this problem)
6. UVa 10583 - Ubiquitous Religions (count disjoint sets after all unions)
7. UVa 10608 - Friends (find the set with the largest element)
8. UVa 10685 - Nature (find the set with the largest element)
9. UVa 11503 - Virtual Friends * (maintain set attribute (size) in rep item)
10. UVa 11690 - Money Matters (check if total money from each member is 0)
• Tree-related Data Structures
1. UVa 00297 - Quadtrees (simple quadtree problem)
2. UVa 01232 - SKYLINE (LA 4108, Singapore07, a simple problem if input
size is small; but since n ≤ 100000, we have to use a Segment Tree; note that
this problem is not about RSQ/RMQ)
3. UVa 11235 - Frequent Values * (range maximum query)
4. UVa 11297 - Census (Quad Tree with updates or use 2D segment tree)
5. UVa 11350 - Stern-Brocot Tree (simple tree data structure question)
6. UVa 11402 - Ahoy, Pirates * (segment tree with lazy updates)
7. UVa 12086 - Potentiometers (LA 2191, Dhaka06; pure dynamic range sum
query problem; solvable with Fenwick Tree or Segment Tree)
8. UVa 12532 - Interval Product * (clever usage of Fenwick/Segment Tree)
Also see: DS as part of the solution of harder problems in Chapter 8

63
2.5. SOLUTION TO NON-STARRED EXERCISES 
c Steven & Felix

2.5 Solution to Non-Starred Exercises


Exercise 2.2.1*: Sub-question 1: First, sort S in O(n log n) and then do an O(n) linear
scan starting from the second element to check if an integer and the previous integer are
the same (also read the solution for Exercise 1.2.10, task 4). Sub-question 6: Read the
opening paragraph of Chapter 3 and the detailed discussion in Section 9.29. Solutions for
the other sub-questions are not shown.
Exercise 2.2.2: The answers (except sub-question 7):

1. S & (N − 1)

2. (S & (S − 1)) == 0

3. S & (S − 1)

4. S  (S + 1)

5. S & (S + 1)

6. S  (S − 1)

Exercise 2.3.1: Since the collection is dynamic, we will encounter frequent insertion and
deletion queries. An insertion can potentially change the sort order. If we store the informa-
tion in a static array, we will have to use one O(n) iteration of an insertion sort after each
insertion and deletion (to close the gap in the array). This is inefficient!
Exercise 2.3.2:

1. search(71): root (15) → 23 → 71 (found)


search(7): root (15) → 6 → 7 (found)
search(22): root (15) → 23 → empty left subtree (not found).

2. We will eventually have the same BST as in Figure 2.2.

3. To find the min/max element, we can start from root and keep going left/right until we
encounter a vertex with no left/right subtrees respectively. That vertex is the answer.

4. We will obtain the sorted output: 4, 5, 6, 7, 15, 23, 50, 71. See Section 4.7.2 if you are
not familiar with the inorder tree traversal algorithm.

5. successor(23): Find the minimum element of the subtree rooted at the right of 23,
which is the subtree rooted at 71. The answer is 50.
successor(7): 7 has no right subtree, so 7 must be the maximum of a certain subtree.
That subtree is the subtree rooted at 6. The parent of 6 is 15 and 6 is the left subtree
of 15. By the BST property, 15 must be the successor of 7.
successor(71): 71 is the largest element and has no successor.
Note: The algorithm to find the predecessor of a node is similar.

6. delete(5): We simply remove 5, which is a leaf, from the BST


delete(71): As 71 is an internal vertex with one child, we cannot simply delete 71 as
doing so will disconnect the BST into two components. We can instead reshuffle the
subtree rooted at the parent of 71 (which is 23), causing 23 to has 50 as its right child.

64
CHAPTER 2. DATA STRUCTURES AND LIBRARIES 
c Steven & Felix

7. delete(15): As 15 is a vertex with two children, we cannot simply delete 15 as doing


so will disconnect the BST into three components. To deal with this issue, we need
to find the successor of 15 (which is 23) and use the successor to replace 15. We then
delete the old 23 from the BST (not a problem now). As a note, we can also use
predecessor(key) instead of successor(key) during delete(key) for the case when the
key has two children.

Exercise 2.3.3*: For Sub-task 1, we run inorder traversal in O(n) and see if the values are
sorted. Solutions to other sub-tasks are not shown.
Exercise 2.3.6: The answers:

1. Insert(26): Insert 26 as the left subtree of 3, swap 26 with 3, then swap 26 with 19
and stop. The Max Heap array A now contains {-, 90, 26, 36, 17, 19, 25, 1, 2, 7, 3}.

2. ExtractMax(): Swap 90 (maximum element which will be reported after we fix the


Max Heap property) with 3 (the current bottom-most right-most leaf/the last item in
the Max Heap), swap 3 with 36, swap 3 with 25 and stop. The Max Heap array A
now contains {-, 36, 26, 25, 17, 19, 3, 1, 2, 7}.

Exercise 2.3.7: Yes, check that all indices (vertices) satisfy the Max Heap property.
Exercise 2.3.16: Use the C++ STL set (or Java TreeSet) as it is a balanced BST that
supports O(log n) dynamic insertions and deletions. We can use the inorder traversal to
print the data in the BST in sorted order (simply use C++ iterators or Java Iterators).
Exercise 2.3.17: Use the C++ STL map (Java TreeMap) and a counter variable. A hash
table is also a possible solution but not necessary for programming contests. This trick is
quite frequently used in various (contest) problems. Example usage:

char str[1000];
map<string, int> mapper;
int i, idx;
for (i = idx = 0; i < M; i++) { // idx starts from 0
scanf("%s", &str);
if (mapper.find(str) == mapper.end()) // if this is the first encounter
// alternatively, we can also test if mapper.count(str) is greater than 0
mapper[str] = idx++; // give str the current idx and increase idx
}

Exercise 2.4.1.3: The graph is undirected.


Exercise 2.4.1.4*: Subtask 1: To count the number of vertices of a graph: Adjacency
Matrix/Adjacency List → report the number of rows; Edge List → count the number of
distinct vertices in all edges. To count the number of edges of a graph: Adjacency Matrix
→ sum the number of non-zero entries in every row; Adjacency List → sum the length of all
the lists; Edge List → simply report the number of rows. Solutions to other sub-tasks are
not shown.
Exercise 2.4.2.1: For int numDisjointSets(), use an additional integer counter numSets.
Initially, during UnionFind(N), set numSets = N. Then, during unionSet(i, j), decrease
numSets by one if isSameSet(i, j) returns false. Now, int numDisjointSets() can sim-
ply return the value of numSets.

65
2.5. SOLUTION TO NON-STARRED EXERCISES 
c Steven & Felix

For int sizeOfSet(int i), we use another vi setSize(N) initialized to all ones (each
set has only one element). During unionSet(i, j), update the setSize array by performing
setSize[find(j)] += setSize[find(i)] (or the other way around depending on rank) if
isSameSet(i, j) returns false. Now int sizeOfSet(int i) can simply return the value
of setSize[find(i)];
These two variants have been implemented in ch2 08 unionfind ds.cpp/java.
Exercise 2.4.3.3: RSQ(1, 7) = 167 and RSQ(3, 8) = 139; No, using a Segment Tree is
overkill. There is a simple DP solution that uses an O(n) pre-processing step and takes O(1)
time per RSQ (see Section 9.33).
Exercise 2.4.4.1: 90 - LSOne(90) = (1011010)2 - (10)2 = (1011000)2 = 88 and
90 + LSOne(90) = (1011010)2 + (10)2 = (1011100)2 = 92.
Exercise 2.4.4.2: Simple: shift all indices by one. Index iin the 1-based Fenwick Tree now
refers to index i − 1 in the actual problem.
Exercise 2.4.4.3: Simple: convert the floating point numbers into integers. For the first
task, we can multiply every number by two. For the second case, we can multiply all numbers
by one hundred.
Exercise 2.4.4.4: The cumulative frequency is sorted, thus we can use a binary search.
Study the ‘binary search for the answer’ technique discussed in Section 3.3. The resulting
time complexity is O(log2 n).

66
CHAPTER 2. DATA STRUCTURES AND LIBRARIES 
c Steven & Felix

2.6 Chapter Notes


The basic data structures mentioned in Section 2.2-2.3 can be found in almost every data
structure and algorithm textbook. References to the C++/Java built-in libraries are avail-
able online at: www.cppreference.com and java.sun.com/javase/7/docs/api. Note that
although access to these reference websites are usually provided in programming contests,
we suggest that you try to master the syntax of the most common library operations to
minimize coding time during actual contests!
One exception is perhaps the lightweight set of Boolean (a.k.a bitmask). This unusual
technique is not commonly taught in data structure and algorithm classes, but it is quite
important for competitive programmers as it allows for significant speedups if applied to
certain problems. This data structure appears in various places throughout this book, e.g.
in some iterative brute force and optimized backtracking routines (Section 3.2.2 and Section
8.2.1), DP TSP (Section 3.5.2), DP with bitmask (Section 8.3.1). All of them use bitmasks
instead of vector<boolean> or bitset<size> due to its efficiency. Interested readers
are encouraged to read the book “Hacker’s Delight” [69] that discusses bit manipulation in
further detail.
Extra references for the data structures mentioned in Section 2.4 are as follows. For
Graphs, see [58] and Chapters 22-26 of [7]. For Union-Find Disjoint Sets, see Chapter 21 of
[7]. For Segment Trees and other geometric data structures, see [9]. For the Fenwick Tree,
see [30]. We remark that all our implementation of data structures discussed in Section 2.4
avoid the usage of pointers. We use either arrays or vectors.
With more experience and by reading the source code we have provided, you can master
more tricks in the application of these data structures. Please spend time exploring the source
code provided with this book at sites.google.com/site/stevenhalim/home/material.
There are few more data structures discussed in this book—string-specific data structures
(Suffix Trie/Tree/Array) are discussed in Section 6.6. Yet, there are still many other
data structures that we cannot cover in this book. If you want to do better in programming
contests, please research data structure techniques beyond what we have presented in this
book. For example, AVL Trees, Red Black Trees, or even Splay Trees are useful for
certain problems that require you to implement and augment (add more data to) balanced
BSTs (see Section 9.29). Interval Trees (which are similar to Segment Trees) and Quad
Trees (for partitioning 2D space) are useful to know as their underlying concepts may help
you to solve certain contest problems.
Notice that many of the efficient data structures discussed in this book exhibit the ‘Divide
and Conquer’ strategy (discussed in Section 3.3).

Statistics First Edition Second Edition Third Edition


Number of Pages 12 18 (+50%) 35 (+94%)
Written Exercises 5 12 (+140%) 14+27*=41 (+242%)
Programming Exercises 43 124 (+188%) 132 (+6%)

The breakdown of the number of programming exercises from each section is shown below:

Section Title Appearance % in Chapter % in Book


2.2 Linear DS 79 60% 5%
2.3 Non-Linear DS 30 23% 2%
2.4 Our-own Libraries 23 17% 1%

67
2.6. CHAPTER NOTES 
c Steven & Felix

68
Chapter 3

Problem Solving Paradigms


If all you have is a hammer, everything looks like a nail
— Abraham Maslow, 1962

3.1 Overview and Motivation


In this chapter, we discuss four problem solving paradigms commonly used to attack prob-
lems in programming contests, namely Complete Search (a.k.a Brute Force), Divide and
Conquer, the Greedy approach, and Dynamic Programming. All competitive programmers,
including IOI and ICPC contestants, need to master these problem solving paradigms (and
more) in order to be able to attack a given problem with the appropriate ‘tool’. Hammering
every problem with Brute Force solutions will not enable anyone to perform well in contests.
To illustrate, we discuss four simple tasks below involving an array A containing n ≤ 10K
small integers ≤ 100K (e.g. A = {10, 7, 3, 5, 8, 2, 9}, n = 7) to give an overview of what
happens if we attempt every problem with Brute Force as our sole paradigm.
1. Find the largest and the smallest element of A. (10 and 2 for the given example).
2. Find the k th smallest element in A. (if k = 2, the answer is 3 for the given example).
3. Find the largest gap g such that x, y ∈ A and g = |x − y|. (8 for the given example).
4. Find the longest increasing subsequence of A. ({3, 5, 8, 9} for the given example).
The answer for the first task is simple: Try each element of A and check if it is the current
largest (or smallest) element seen so far. This is an O(n) Complete Search solution.
The second task is a little harder. We can use the solution above to find the smallest
value and replace it with a large value (e.g. 1M) to ‘delete’ it. We can then proceed to find
the smallest value again (the second smallest value in the original array) and replace it with
1M. Repeating this process k times, we will find the k th smallest value. This works, but
if k = n2 (the median), this Complete Search solution runs in O( n2 × n) = O(n2 ). Instead,
we can sort the array A in O(n log n), returning the answer simply as A[k-1]. However, a
better solution for a small number of queries is the expected O(n) solution shown in Section
9.29. The O(n log n) and O(n) solutions above are Divide and Conquer solutions.
For the third task, we can similarly consider all possible two integers x and y in A, checking
if the gap between them is the largest for each pair. This Complete Search approach runs
in O(n2 ). It works, but is slow and inefficient. We can prove that g can be obtained by
finding the difference between the smallest and largest elements of A. These two integers can
be found with the solution of the first task in O(n). No other combination of two integers
in A can produce a larger gap. This is a Greedy solution.
For the fourth task, trying all O(2n ) possible subsequences to find the longest increasing
one is not feasible for all n ≤ 10K. In Section 3.5.2, we discuss a simple O(n2 ) Dynamic
Programming solution and also the faster O(n log k) Greedy solution for this task.

69
3.2. COMPLETE SEARCH 
c Steven & Felix

Here is some advice for this chapter: Please do not just memorize the solutions for each
problem discussed, but instead remember and internalize the thought process and problem
solving strategies used. Good problem solving skills are more important than memorized
solutions for well-known Computer Science problems when dealing with (often creative and
novel) contest problems.

3.2 Complete Search


The Complete Search technique, also known as brute force or recursive backtracking, is a
method for solving a problem by traversing the entire (or part of the) search space to obtain
the required solution. During the search, we are allowed to prune (that is, choose not to
explore) parts of the search space if we have determined that these parts have no possibility
of containing the required solution.
In programming contests, a contestant should develop a Complete Search solution when
there is clearly no other algorithm available (e.g. the task of enumerating all permutations
of {0, 1, 2, . . . , N − 1} clearly requires O(N!) operations) or when better algorithms exist,
but are overkill as the input size happens to be small (e.g. the problem of answering Range
Minimum Queries as in Section 2.4.3 but on static arrays with N ≤ 100 is solvable with an
O(N) loop for each query).
In ICPC, Complete Search should be the first solution considered as it is usually easy
to come up with such a solution and to code/debug it. Remember the ‘KISS’ principle:
Keep It Short and Simple. A bug-free Complete Search solution should never receive the
Wrong Answer (WA) response in programming contests as it explores the entire search space.
However, many programming problems do have better-than-Complete-Search solutions as
illustrated in the Section 3.1. Thus a Complete Search solution may receive a Time Limit
Exceeded (TLE) verdict. With proper analysis, you can determine the likely outcome (TLE
versus AC) before attempting to code anything (Table 1.4 in Section 1.2.3 is a good starting
point). If a Complete Search is likely to pass the time limit, then go ahead and implement
one. This will then give you more time to work on harder problems in which Complete
Search will be too slow.
In IOI, you will usually need better problem solving techniques as Complete Search
solutions are usually only rewarded with very small fractions of the total score in the subtask
scoring schemes. Nevertheless, Complete Search should be used when you cannot come up
with a better solution—it will at least enable you to score some marks.
Sometimes, running Complete Search on small instances of a challenging problem can
help us to understand its structure through patterns in the output (it is possible to visualize
the pattern for some problems) that can be exploited to design a faster algorithm. Some
combinatorics problems in Section 5.4 can be solved this way. Then, the Complete Search
solution can also act as a verifier for small instances, providing an additional check for the
faster but non-trivial algorithm that you develop.
After reading this section, you may have the impression that Complete Search only works
for ‘easy problems’ and it is usually not the intended solution for ‘harder problems’. This is
not entirely true. There exist hard problems that are only solvable with creative Complete
Search algorithms. We have reserved those problems for Section 8.2.
In the next two sections, we give several (easier) examples of this simple yet possibly
challenging paradigm. In Section 3.2.1, we give examples that are implemented iteratively.
In Section 3.2.2, we give examples on solutions that are implemented recursively (with back-
tracking). Finally, in Section 3.2.3, we provide a few tips to give your solution, especially
your Complete Search solution, a better chance to pass the required Time Limit.

70
CHAPTER 3. PROBLEM SOLVING PARADIGMS 
c Steven & Felix

3.2.1 Iterative Complete Search


Iterative Complete Search (Two Nested Loops: UVa 725 - Division)
Abridged problem statement: Find and display all pairs of 5-digit numbers that collectively
use the digits 0 through 9 once each, such that the first number divided by the second is
equal to an integer N, where 2 ≤ N ≤ 79. That is, abcde / fghij = N, where each letter
represents a different digit. The first digit of one of the numbers is allowed to be zero, e.g.
for N = 62, we have 79546 / 01283 = 62; 94736 / 01528 = 62.
Quick analysis shows that fghij can only range from 01234 to 98765 which is at most
≈ 100K possibilities. An even better bound for fghij is the range 01234 to 98765 / N,
which has at most ≈ 50K possibilities for N = 2 and becomes smaller with increasing N. For
each attempted fghij, we can get abcde from fghij * N and then check if all 10 digits are
different. This is a doubly-nested loop with a time complexity of at most ≈ 50K ×10 = 500K
operations per test case. This is small. Thus, an iterative Complete Search is feasible. The
main part of the code is shown below (we use a fancy bit manipulation trick shown in Section
2.2 to determine digit uniqueness):

for (int fghij = 1234; fghij <= 98765 / N; fghij++) {


int abcde = fghij * N; // this way, abcde and fghij are at most 5 digits
int tmp, used = (fghij < 10000); // if digit f=0, then we have to flag it
tmp = abcde; while (tmp) { used |= 1 << (tmp % 10); tmp /= 10; }
tmp = fghij; while (tmp) { used |= 1 << (tmp % 10); tmp /= 10; }
if (used == (1<<10) - 1) // if all digits are used, print it
printf("%0.5d / %0.5d = %d\n", abcde, fghij, N);
}

Iterative Complete Search (Many Nested Loops: UVa 441 - Lotto)


In programming contests, problems that are solvable with a single loop are usually considered
easy. Problems which require doubly-nested iterations like UVa 725 - Division above are more
challenging but they are not necessarily considered difficult. Competitive programmers must
be comfortable writing code with more than two nested loops.
Let’s take a look at UVa 441 which can be summarized as follows: Given 6 < k < 13
integers, enumerate all possible subsets of size 6 of these integers in sorted order.
Since the size of the required subset is always 6 and the output has to be sorted lexico-
graphically (the input is already sorted), the easiest solution is to use six nested loops as
shown below. Note that even in the largest test case when k = 12, these six nested loops
will only produce 12 C6 = 924 lines of output. This is small.

for (int i = 0; i < k; i++) // input: k sorted integers


scanf("%d", &S[i]);
for (int a = 0 ; a < k - 5; a++) // six nested loops!
for (int b = a + 1; b < k - 4; b++)
for (int c = b + 1; c < k - 3; c++)
for (int d = c + 1; d < k - 2; d++)
for (int e = d + 1; e < k - 1; e++)
for (int f = e + 1; f < k ; f++)
printf("%d %d %d %d %d %d\n",S[a],S[b],S[c],S[d],S[e],S[f]);

71
3.2. COMPLETE SEARCH 
c Steven & Felix

Iterative Complete Search (Loops + Pruning: UVa 11565 - Simple Equations)


Abridged problem statement: Given three integers A, B, and C (1 ≤ A, B, C ≤ 10000),
find three other distinct integers x, y, and z such that x + y + z = A, x × y × z = B, and
x2 + y 2 + z 2 = C.
The third equation x2 + y 2 + z 2 = C is a good starting point. Assuming that C has
the largest value of 10000 and y and z are one and two (x, y, z have to be distinct), then
the possible range of values for x is [−100 . . . 100]. We can use the same reasoning to get a
similar range for y and z. We can then write the following triply-nested iterative solution
below that requires 201 × 201 × 201 ≈ 8M operations per test case.

bool sol = false; int x, y, z;


for (x = -100; x <= 100; x++)
for (y = -100; y <= 100; y++)
for (z = -100; z <= 100; z++)
if (y != x && z != x && z != y && // all three must be different
x + y + z == A && x * y * z == B && x * x + y * y + z * z == C) {
if (!sol) printf("%d %d %d\n", x, y, z);
sol = true; }

Notice the way a short circuit AND was used to speed up the solution by enforcing a
lightweight check on whether x, y, and z are all different before we check the three formulas.
The code shown above already passes the required time limit for this problem, but we can
do better. We can also use the second
√ equation x × y × z = B and assume that x = y = z
3
to obtain x × x × x < B or x < B. The new range of x is [−22 . . . 22]. We can also prune
the search space by using if statements to execute only some of the (inner) loops, or use
break and/or continue statements to stop/skip loops. The code shown below is now much
faster than the code shown above (there are a few other optimizations required to solve the
extreme version of this problem: UVa 11571 - Simple Equations - Extreme!!):

bool sol = false; int x, y, z;


for (x = -22; x <= 22 && !sol; x++) if (x * x <= C)
for (y = -100; y <= 100 && !sol; y++) if (y != x && x * x + y * y <= C)
for (z = -100; z <= 100 && !sol; z++)
if (z != x && z != y &&
x + y + z == A && x * y * z == B && x * x + y * y + z * z == C) {
printf("%d %d %d\n", x, y, z);
sol = true; }

Iterative Complete Search (Permutations: UVa 11742 - Social Constraints)


Abridged problem statement: There are 0 < n ≤ 8 movie goers. They will sit in the front
row in n consecutive open seats. There are 0 ≤ m ≤ 20 seating constraints among them, i.e.
movie goer a and movie goer b must be at most (or at least) c seats apart. The question is
simple: How many possible seating arrangements are there?
The key part to solve this problem is in realizing that we have to explore all permutations
(seating arrangements). Once we realize this fact, we can derive this simple O(m × n!)
‘filtering’ solution. We set counter = 0 and then try all possible n! permutations. We
increase the counter by 1 if the current permutation satisfies all m constraints. When all n!
permutations have been examined, we output the final value of counter. As the maximum

72
CHAPTER 3. PROBLEM SOLVING PARADIGMS 
c Steven & Felix

n is 8 and maximum m is 20, the largest test case will still only require 20 × 8! = 806400
operations—a perfectly viable solution.
If you have never written an algorithm to generate all permutations of a set of numbers
(see Exercise 1.2.3, task 7), you may still be unsure about how to proceed. The simple
C++ solution is shown below.
#include <algorithm> // next_permutation is inside this C++ STL
// the main routine
int i, n = 8, p[8] = {0, 1, 2, 3, 4, 5, 6, 7}; // the first permutation
do { // try all possible O(n!) permutations, the largest input 8! = 40320
... // check the given social constraint based on ‘p’ in O(m)
} // the overall time complexity is thus O(m * n!)
while (next_permutation(p, p + n)); // this is inside C++ STL <algorithm>

Iterative Complete Search (Subsets: UVa 12455 - Bars)


Abridged problem statement1 : Given a list l containing 1 ≤ n ≤ 20 integers, is there a
subset of list l that sums to another given integer X?
We can try all 2n possible subsets of integers, sum the selected integers for each subset in
O(n), and see if the sum of the selected integers equals to X. The overall time complexity
is thus O(n × 2n ). For the largest test case when n = 20, this is just 20 × 220 ≈ 21M. This
is ‘large’ but still viable (for reason described below).
If you have never written an algorithm to generate all subsets of a set of numbers (see
Exercise 1.2.3, task 8), you may still be unsure how to proceed. An easy solution is to
use the binary representation of integers from 0 to 2n − 1 to describe all possible subsets.
If you are not familiar with bit manipulation techniques, see Section 2.2. The solution can
be written in simple C/C++ shown below (also works in Java). Since bit manipulation
operations are (very) fast, the required 21M operations for the largest test case are still
doable in under a second. Note: A faster implementation is possible (see Section 8.2.1).
// the main routine, variable ‘i’ (the bitmask) has been declared earlier
for (i = 0; i < (1 << n); i++) { // for each subset, O(2^n)
sum = 0;
for (int j = 0; j < n; j++) // check membership, O(n)
if (i & (1 << j)) // test if bit ‘j’ is turned on in subset ‘i’?
sum += l[j]; // if yes, process ‘j’
if (sum == X) break; // the answer is found: bitmask ‘i’
}

Exercise 3.2.1.1: For the solution of UVa 725, why is it better to iterate through fghij
and not through abcde?
Exercise 3.2.1.2: Does a 10! algorithm that permutes abcdefghij work for UVa 725?
Exercise 3.2.1.3*: Java does not have a built-in next permutation function yet. If you
are a Java user, write your own recursive backtracking routine to generate all permutations!
This is similar to the recursive backtracking for the 8-Queens problem.
Exercise 3.2.1.4*: How would you solve UVa 12455 if 1 ≤ n ≤ 30 and each integer can be
as big as 1000000000? Hint: See Section 8.2.4.

1
This is also known as the ‘Subset Sum’ problem, see Section 3.5.3.

73
3.2. COMPLETE SEARCH 
c Steven & Felix

3.2.2 Recursive Complete Search


Simple Backtracking: UVa 750 - 8 Queens Chess Problem
Abridged problem statement: In chess (with an 8 × 8 board), it is possible to place eight
queens on the board such that no two queens attack each other. Determine all such possible
arrangements given the position of one of the queens (i.e. coordinate (a, b) must contain a
queen). Output the possibilities in lexicographical (sorted) order.
The most naı̈ve solution is to enumerate all combinations of 8 different cells out of the
8 × 8 = 64 possible cells in a chess board and see if the 8 queens can be placed at these
positions without conflicts. However, there are 64 C8 ≈ 4B such possibilities—this idea is not
even worth trying.
A better but still naı̈ve solution is to realize that each queen can only occupy one column,
so we can put exactly one queen in each column. There are only 88 ≈ 17M possibilities now,
down from 4B. This is still a ‘borderline’-passing solution for this problem. If we write a
Complete Search like this, we are likely to receive the Time Limit Exceeded (TLE) verdict
especially if there are multiple test cases. We can still apply the few more easy optimizations
described below to further reduce the search space.
We know that no two queens can share the same column or the
same row. Using this, we can further simplify the original problem
to the problem of finding valid permutations of 8! row positions.
The value of row[i] describes the row position of the queen in
column i. Example: row = {1, 3, 5, 7, 2, 0, 6, 4} as in
Figure 3.1 is one of the solutions for this problem; row[0] = 1
implies that the queen in column 0 is placed in row 1, and so
on (the index starts from 0 in this example). Modeled this way,
the search space goes down from 88 ≈ 17M to 8! ≈ 40K. This Figure 3.1: 8-Queens
solution is already fast enough, but we can still do more.
We also know that no two queens can share any of the two diagonal lines. Let queen A be
at (i, j) and queen B be at (k, l). They attack each other iff abs(i-k) == abs(j-l).
This formula means that the vertical and horizontal distances between these two queens are
equal, i.e. queen A and B lie on one of each other’s two diagonal lines.
A recursive backtracking solution places the queens one by one in columns 0 to 7, observ-
ing all the constraints above. Finally, if a candidate solution is found, check if at least one
of the queens satisfies the input constraints, i.e. row[b] == a. This sub (i.e. lower than)
O(n!) solution will obtain an AC verdict.
We provide our implementation below. If you have never written a recursive backtracking
solution before, please scrutinize it and perhaps re-code it in your own coding style.

#include <cstdlib> // we use the int version of ’abs’


#include <cstdio>
#include <cstring>
using namespace std;

int row[8], TC, a, b, lineCounter; // ok to use global variables

bool place(int r, int c) {


for (int prev = 0; prev < c; prev++) // check previously placed queens
if (row[prev] == r || (abs(row[prev] - r) == abs(prev - c)))
return false; // share same row or same diagonal -> infeasible
return true; }

74
CHAPTER 3. PROBLEM SOLVING PARADIGMS 
c Steven & Felix

void backtrack(int c) {
if (c == 8 && row[b] == a) { // candidate sol, (a, b) has 1 queen
printf("%2d %d", ++lineCounter, row[0] + 1);
for (int j = 1; j < 8; j++) printf(" %d", row[j] + 1);
printf("\n"); }
for (int r = 0; r < 8; r++) // try all possible row
if (place(r, c)) { // if can place a queen at this col and row
row[c] = r; backtrack(c + 1); // put this queen here and recurse
} }

int main() {
scanf("%d", &TC);
while (TC--) {
scanf("%d %d", &a, &b); a--; b--; // switch to 0-based indexing
memset(row, 0, sizeof row); lineCounter = 0;
printf("SOLN COLUMN\n");
printf(" # 1 2 3 4 5 6 7 8\n\n");
backtrack(0); // generate all possible 8! candidate solutions
if (TC) printf("\n");
} } // return 0;

Source code: ch3 01 UVa750.cpp/java

More Challenging Backtracking: UVa 11195 - Another n-Queen Problem


Abridged problem statement: Given an n × n chessboard (3 < n < 15) where some of the
cells are bad (queens cannot be placed on those bad cells), how many ways can you place n
queens in the chessboard so that no two queens attack each other? Note: Bad cells cannot
be used to block queens’ attack.
The recursive backtracking code that we have presented above is not fast enough for
n = 14 and no bad cells, the worst possible test case for this problem. The sub-O(n!)
solution presented earlier is still OK for n = 8 but not for n = 14. We have to do better.
The major issue with the previous n-queens code is that it is quite slow when checking
whether the position of a new queen is valid since we compare the new queen’s position with
the previous c-1 queens’ positions (see function bool place(int r, int c)). It is better
to store the same information with three boolean arrays (we use bitsets for now):

bitset<30> rw, ld, rd; // for the largest n = 14, we have 27 diagonals

Initially all n rows (rw), 2 × n − 1 left diagonals (ld), and 2 × n − 1 right diagonals (rd) are
unused (these three bitsets are all set to false). When a queen is placed at cell (r, c),
we flag rw[r] = true to disallow this row from being used again. Furthermore, all (a, b)
where abs(r - a) = abs(c - b) also cannot be used anymore. There are two possibilities
after removing the abs function: r - c = a - b and r + c = a + b. Note that r + c and
r - c represent indices for the two diagonal axes. As r - c can be negative, we add an
offset of n - 1 to both sides of the equation so that r - c + n - 1 = a - b + n - 1. If a
queen is placed on cell (r, c), we flag ld[r - c + n - 1] = true and rd[r + c] = true
to disallow these two diagonals from being used again. With these additional data structures
and the additional problem-specific constraint in UVa 11195 (board[r][c] cannot be a bad
cell), we can extend our code to become:

75
3.2. COMPLETE SEARCH 
c Steven & Felix

void backtrack(int c) {
if (c == n) { ans++; return; } // a solution
for (int r = 0; r < n; r++) // try all possible row
if (board[r][c] != ’*’ && !rw[r] && !ld[r - c + n - 1] && !rd[r + c]) {
rw[r] = ld[r - c + n - 1] = rd[r + c] = true; // flag off
backtrack(c + 1);
rw[r] = ld[r - c + n - 1] = rd[r + c] = false; // restore
} }

Visualization: www.comp.nus.edu.sg/∼stevenha/visualization/recursion.html

Exercise 3.2.2.1: The code shown for UVa 750 can be further optimized by pruning the
search when ‘row[b] != a’ earlier during the recursion (not only when c == 8). Modify it!
Exercise 3.2.2.2*: Unfortunately, the updated solution presented using bitsets: rw, ld,
and rd will still obtain a TLE for UVa 11195 - Another n-Queen Problem. We need to
further speed up the solution using bitmask techniques and another way of using the left
and right diagonal constraints. This solution will be discussed in Section 8.2.1. For now,
use the (non Accepted) idea presented here for UVa 11195 to speed up the code for UVa 750
and two more similar problems: UVa 167 and 11085!

3.2.3 Tips
The biggest gamble in writing a Complete Search solution is whether it will or will not be
able to pass the time limit. If the time limit is 10 seconds (online judges do not usually
use large time limits for efficient judging) and your program currently runs in ≈ 10 seconds
on several (can be more than one) test cases with the largest input size as specified in the
problem description, yet your code is still judged to be TLE, you may want to tweak the
‘critical code’2 in your program instead of re-solving the problem with a faster algorithm
which may not be easy to design.
Here are some tips that you may want to consider when designing your Complete Search
solution for a certain problem to give it a higher chance of passing the Time Limit. Writing
a good Complete Search solution is an art in itself.

Tip 1: Filtering versus Generating


Programs that examine lots of (if not all) candidate solutions and choose the ones that are
correct (or remove the incorrect ones) are called ‘filters’, e.g. the naı̈ve 8-queens solver with
8
64 C8 and 8 time complexity, the iterative solution for UVa 725 and UVa 11742, etc. Usually
‘filter’ programs are written iteratively.
Programs that gradually build the solutions and immediately prune invalid partial solu-
tions are called ‘generators’, e.g. the improved recursive 8-queens solver with its sub-O(n!)
complexity plus diagonal checks. Usually, ‘generator’ programs are easier to implement when
written recursively as it gives us greater flexibility for pruning the search space.
Generally, filters are easier to code but run slower, given that it is usually far more
difficult to prune more of the search space iteratively. Do the math (complexity analysis) to
see if a filter is good enough or if you need to create a generator.
2
It is said that every program spends most of its time in only about 10% of its code—the critical code.

76
CHAPTER 3. PROBLEM SOLVING PARADIGMS 
c Steven & Felix

Tip 2: Prune Infeasible/Inferior Search Space Early


When generating solutions using recursive backtracking (see the tip no 1 above), we may
encounter a partial solution that will never lead to a full solution. We can prune the search
there and explore other parts of the search space. Example: The diagonal check in the
8-queens solution above. Suppose we have placed a queen at row[0] = 2. Placing the
next queen at row[1] = 1 or row[1] = 3 will cause a diagonal conflict and placing the
next queen at row[1] = 2 will cause a row conflict. Continuing from any of these infeasible
partial solutions will never lead to a valid solution. Thus we can prune these partial solutions
at this juncture and concentrate only on the other valid positions: row[1] = {0, 4, 5, 6,
7}, thus reducing the overall runtime. As a rule of thumb, the earlier you can prune the
search space, the better.
In other problems, we may be able to compute the ‘potential worth’ of a partial (and
still valid) solution. If the potential worth is inferior to the worth of the current best found
valid solution so far, we can prune the search there.

Tip 3: Utilize Symmetries


Some problems have symmetries and we should try to exploit symmetries to reduce execu-
tion time! In the 8-queens problem, there are 92 solutions but there are only 12 unique (or
fundamental/canonical) solutions as there are rotational and line symmetries in the prob-
lem. You can utilize this fact by only generating the 12 unique solutions and, if needed,
generate the whole 92 by rotating and reflecting these 12 unique solutions. Example: row =
{7-1, 7-3, 7-5, 7-7, 7-2, 7-0, 7-6, 7-4} = {6, 4, 2, 0, 5, 7, 1, 3} is the hor-
izontal reflection of the configuration in Figure 3.1.
However, we have to remark that it is true that sometimes considering symmetries can
actually complicate the code. In competitive programming, this is usually not the best way
(we want shorter code to minimize bugs). If the gain obtained by dealing with symmetry is
not significant in solving the problem, just ignore this tip.

Tip 4: Pre-Computation a.k.a. Pre-Calculation


Sometimes it is helpful to generate tables or other data structures that accelerate the lookup
of a result prior to the execution of the program itself. This is called Pre-Computation, in
which one trades memory/space for time. However, this technique can rarely be used for
recent programming contest problems.
For example, since we know that there are only 92 solutions in the standard 8-queens
chess problem, we can create a 2D array int solution[92][8] and then fill it with all
92 valid permutations of the 8-queens row positions! That is, we can create a generator
program (which takes some time to run) to fill this 2D array solution. Afterwards, we can
write another program to simply and quickly print the correct permutations within the 92
pre-calculated configurations that satisfy the problem constraints.

Tip 5: Try Solving the Problem Backwards


Some contest problems look far easier when they are solved ‘backwards’ [53] (from a less
obvious angle) than when they are solved using a frontal attack (from the more obvious
angle). Be prepared to attempt unconventional approaches to problems.
This tip is best illustrated using an example: UVa 10360 - Rat Attack: Imagine a 2D
array (up to 1024 × 1024) containing rats. There are n ≤ 20000 rats spread across the cells.
Determine which cell (x, y) should be gas-bombed so that the number of rats killed in

77
3.2. COMPLETE SEARCH 
c Steven & Felix

a square box (x-d, y-d) to (x+d, y+d) is maximized. The value d is the power of the
gas-bomb (d ≤ 50), see Figure 3.2.
An immediate solution is to attack this problem in the most obvious fashion possible:
bomb each of the 10242 cells and select the most effective location. For each bombed cell
(x, y), we can perform an O(d2) scan to count the number of rats killed within the square-
bombing radius. For the worst case, when the array has size 10242 and d = 50, this takes
10242 × 502 = 2621M operations. TLE3 !
Another option is to attack this problem backwards: Create
an array int killed[1024][1024]. For each rat population
at coordinate (x, y), add it to killed[i][j], where |i − x| ≤
d and |j − y| ≤ d. This is because if a bomb was placed at
(i, j), the rats at coordinate (x, y) will be killed. This
pre-processing takes O(n × d2 ) operations. Then, to determine
the most optimal bombing position, we can simply find the
coordinate of the highest entry in array killed, which can be
done in 10242 operations. This approach only requires 20000 ×
502 + 10242 = 51M operations for the worst test case (n =
20000, d = 50), ≈ 51 times faster than the frontal attack! This Figure 3.2: UVa 10360 [47]
is an AC solution.

Tip 6: Optimizing Your Source Code


There are many tricks that you can use to optimize your code. Understanding computer
hardware and how it is organized, especially the I/O, memory, and cache behavior, can help
you design better code. Some examples (not exhaustive) are shown below:

1. A biased opinion: Use C++ instead of Java. An algorithm implemented using C++
usually runs faster than the one implemented in Java in many online judges, including
UVa [47]. Some programming contests give Java users extra time to account for the
difference in performance.
2. For C/C++ users, use the faster C-style scanf/printf rather than cin/cout. For
Java users, use the faster BufferedReader/BufferedWriter classes as follows:

BufferedReader br = new BufferedReader( // speedup


new InputStreamReader(System.in));
// Note: String splitting and/or input parsing is needed afterwards

PrintWriter pr = new PrintWriter(new BufferedWriter( // speedup


new OutputStreamWriter(System.out)));
// PrintWriter allows us to use the pr.printf() function
// do not forget to call pr.close() before exiting your Java program

3. Use the expected O(n log n) but cache-friendly quicksort in C++ STL algorithm::sort
(part of ‘introsort’) rather than the true O(n log n) but non cache-friendly heapsort (its
root-to-leaf/leaf-to-root operations span a wide range of indices—lots of cache misses).
4. Access a 2D array in a row major fashion (row by row) rather than in a column major
fashion—multidimensional arrays are stored in a row-major order in memory.
3
Although 2013 CPU can compute ≈ 100M operations in a few seconds, 2621M operations will still take
too long in a contest environment.

78
CHAPTER 3. PROBLEM SOLVING PARADIGMS 
c Steven & Felix

5. Bit manipulation on the built-in integer data types (up to the 64-bit integer) is more
efficient than index manipulation in an array of booleans (see bitmask in Section 2.2).
If we need more than 64 bits, use the C++ STL bitset rather than vector<bool>
(e.g. for Sieve of Eratosthenes in Section 5.5.1).

6. Use lower level data structures/types at all times if you do not need the extra func-
tionality in the higher level (or larger) ones. For example, use an array with a slightly
larger size than the maximum size of input instead of using resizable vectors. Also,
use 32-bit ints instead of 64-bit long longs as the 32-bit int is faster in most 32-bit
online judge systems.

7. For Java, use the faster ArrayList (and StringBuilder) rather than Vector (and
StringBuffer). Java Vectors and StringBuffers are thread safe but this feature
is not needed in competitive programming. Note: In this book, we will stick with
Vectors to avoid confusing bilingual C++ and Java readers who use both the C++
STL vector and Java Vector.

8. Declare most data structures (especially the bulky ones, e.g. large arrays) once by
placing them in global scope. Allocate enough memory to deal with the largest input
of the problem. This way, we do not have to pass the data structures around as function
arguments. For problems with multiple test cases, simply clear/reset the contents of
the data structure before dealing with each test case.

9. When you have the option to write your code either iteratively or recursively, choose the
iterative version. Example: The iterative C++ STL next permutation and iterative
subset generation techniques using bitmask shown in Section 3.2.1 are (far) faster than
if you write similar routines recursively (mainly due to overheads in function calls).

10. Array access in (nested) loops can be slow. If you have an array A and you frequently
access the value of A[i] (without changing it) in (nested) loops, it may be beneficial
to use a local variable temp = A[i] and works with temp instead.

11. In C/C++, appropriate usage of macros or inline functions can reduce runtime.

12. For C++ users: Using C-style character arrays will yield faster execution than when
using the C++ STL string. For Java users: Be careful with String manipulation as
Java String objects are immutable. Operations on Java Strings can thus be very
slow. Use Java StringBuilder instead.

Browse the Internet or relevant books (e.g. [69]) to find (much) more information on how to
speed up your code. Practice this ‘code hacking skill’ by choosing a harder problem in UVa
online judge where the runtime of the best solution is not 0.000s. Submit several variants of
your Accepted solution and check the runtime differences. Adopt hacking modification that
consistently gives you faster runtime.

Tip 7: Use Better Data Structures & Algorithms :)


No kidding. Using better data structures and algorithms will always outperform any opti-
mizations mentioned in Tips 1-6 above. If you are sure that you have written your fastest
Complete Search code, but it is still judged as TLE, abandon the Complete Search approach.

79
3.2. COMPLETE SEARCH 
c Steven & Felix

Remarks About Complete Search in Programming Contests


The main source of the ‘Complete Search’ material in this chapter is the USACO training
gateway [48]. We have adopted the name ‘Complete Search’ rather than ‘Brute-Force’ (with
its negative connotations) as we believe that some Complete Search solutions can be clever
and fast. We feel that the term ‘clever Brute-Force’ is also a little self-contradictory.
If a problem is solvable by Complete Search, it will also be clear when to use the iterative
or recursive backtracking approaches. Iterative approaches are used when one can derive the
different states easily with some formula relative to a certain counter and (almost) all states
have to be checked, e.g. scanning all the indices of an array, enumerating (almost) all possible
subsets of a small set, generating (almost) all permutations, etc. Recursive Backtracking is
used when it is hard to derive the different states with a simple index and/or one also wants
to (heavily) prune the search space, e.g. the 8-queens chess problem. If the search space
of a problem that is solvable with Complete Search is large, then recursive backtracking
approaches that allow early pruning of infeasible sections of the search space are usually
used. Pruning in iterative Complete Searches is not impossible but usually difficult.
The best way to improve your Complete Search skills is to solve more Complete Search
problems. We have provided a list of such problems, separated into several categories be-
low. Please attempt as many as possible, especially those that are highlighted with the
must try * indicator. Later in Section 3.5, readers will encounter further examples of re-
cursive backtracking, but with the addition of the ‘memoization’ technique.
Note that we will discuss some more advanced search techniques later in Section 8.2,
e.g. using bit manipulation in recursive backtracking, harder state-space search, Meet in
the Middle, A* Search, Depth Limited Search (DLS), Iterative Deepening Search (IDS), and
Iterative Deepening A* (IDA*).

Programming Exercises solvable using Complete Search:

• Iterative (One Loop, Linear Scan)


1. UVa 00102 - Ecological Bin Packing (just try all 6 possible combinations)
2. UVa 00256 - Quirksome Squares (brute force, math, pre-calculate-able)
3. UVa 00927 - Integer Sequence from ... * (use sum of arithmetic series)
4. UVa 01237 - Expert Enough * (LA 4142, Jakarta08, input is small)
5. UVa 10976 - Fractions Again ? * (total solutions is asked upfront; there-
fore do brute force twice)
6. UVa 11001 - Necklace (brute force math, maximize function)
7. UVa 11078 - Open Credit System (one linear scan)
• Iterative (Two Nested Loops)
1. UVa 00105 - The Skyline Problem (height map, sweep left-right)
2. UVa 00347 - Run, Run, Runaround ... (simulate the process)
3. UVa 00471 - Magic Numbers (somewhat similar to UVa 725)
4. UVa 00617 - Nonstop Travel (try all integer speeds from 30 to 60 mph)
5. UVa 00725 - Division (elaborated in this section)
6. UVa 01260 - Sales * (LA 4843, Daejeon10, check all)
7. UVa 10041 - Vito’s Family (try all possible location of Vito’s House)
8. UVa 10487 - Closest Sums * (sort and then do O(n2 ) pairings)

80
CHAPTER 3. PROBLEM SOLVING PARADIGMS 
c Steven & Felix

9. UVa 10730 - Antiarithmetic? (2 nested loops with pruning can pass possibly
pass the weaker test cases; note that this brute force solution is too slow for
the larger test data generated in the solution of UVa 11129)
10. UVa 11242 - Tour de France * (plus sorting)
11. UVa 12488 - Start Grid (2 nested loops; simulate overtaking process)
12. UVa 12583 - Memory Overflow (2 nested loops; be careful of overcounting)
• Iterative (Three Or More Nested Loops, Easier)
1. UVa 00154 - Recycling (3 nested loops)
2. UVa 00188 - Perfect Hash (3 nested loops, until the answer is found)
3. UVa 00441 - Lotto * (6 nested loops)
4. UVa 00626 - Ecosystem (3 nested loops)
5. UVa 00703 - Triple Ties: The ... (3 nested loops)
6. UVa 00735 - Dart-a-Mania * (3 nested loops, then count)
7. UVa 10102 - The Path in the ... * (4 nested loops will do, we do not
need BFS; get max of minimum Manhattan distance from a ‘1’ to a ‘3’.)
8. UVa 10502 - Counting Rectangles (6 nested loops, rectangle, not too hard)
9. UVa 10662 - The Wedding (3 nested loops)
10. UVa 10908 - Largest Square (4 nested loops, square, not too hard)
11. UVa 11059 - Maximum Product (3 nested loops, input is small)
12. UVa 11975 - Tele-loto (3 nested loops, simulate the game as asked)
13. UVa 12498 - Ant’s Shopping Mall (3 nested loops)
14. UVa 12515 - Movie Police (3 nested loops)
• Iterative (Three-or-More Nested Loops, Harder)
1. UVa 00253 - Cube painting (try all, similar problem in UVa 11959)
2. UVa 00296 - Safebreaker (try all 10000 possible codes, 4 nested loops, use
similar solution as ‘Master-Mind’ game)
3. UVa 00386 - Perfect Cubes (4 nested loops with pruning)
4. UVa 10125 - Sumsets (sort; 4 nested loops; plus binary search)
5. UVa 10177 - (2/3/4)-D Sqr/Rects/... (2/3/4 nested loops, precalculate)
6. UVa 10360 - Rat Attack (also solvable using 10242 DP max sum)
7. UVa 10365 - Blocks (use 3 nested loops with pruning)
8. UVa 10483 - The Sum Equals ... (2 nested loops for a, b, derive c from a, b;
there are 354 answers for range [0.01 .. 255.99]; similar with UVa 11236)
9. UVa 10660 - Citizen attention ... * (7 nested loops, Manhattan distance)
10. UVa 10973 - Triangle Counting (3 nested loops with pruning)
11. UVa 11108 - Tautology (5 nested loops, try all 25 = 32 values with pruning)
12. UVa 11236 - Grocery Store * (3 nested loops for a, b, c; derive d from
a, b, c; check if you have 949 lines of output)
13. UVa 11342 - Three-square (pre-calculate squared values from 02 to 2242 , use
3 nested loops to generate the answers; use map to avoid duplicates)
14. UVa 11548 - Blackboard Bonanza (4 nested loops, string, pruning)
15. UVa 11565 - Simple Equations * (3 nested loops with pruning)
16. UVa 11804 - Argentina (5 nested loops)
17. UVa 11959 - Dice (try all possible dice positions, compare with the 2nd one)
Also see Mathematical Simulation in Section 5.2

81
3.2. COMPLETE SEARCH 
c Steven & Felix

• Iterative (Fancy Techniques)


1. UVa 00140 - Bandwidth (max n is just 8, use next permutation; the algo-
rithm inside next permutation is iterative)
2. UVa 00234 - Switching Channels (use next permutation, simulation)
3. UVa 00435 - Block Voting (only 220 possible coalition combinations)
4. UVa 00639 - Don’t Get Rooked (generate 216 combinations and prune)
5. UVa 01047 - Zones * (LA 3278, WorldFinals Shanghai05, notice that
n ≤ 20 so that we can try all possible subsets of towers to be taken; then
apply inclusion-exclusion principle to avoid overcounting)
6. UVa 01064 - Network (LA 3808, WorldFinals Tokyo07, permutation of up
to 5 messages, simulation, mind the word ‘consecutive’)
7. UVa 11205 - The Broken Pedometer (try all 215 bitmask)
8. UVa 11412 - Dig the Holes (next permutation, find one possibility from 6!)
9. UVa 11553 - Grid Game * (solve by trying all n! permutations; you can
also use DP + bitmask, see Section 8.3.1, but it is overkill)
10. UVa 11742 - Social Constraints (discussed in this section)
11. UVa 12249 - Overlapping Scenes (LA 4994, KualaLumpur10, try all permu-
tations, a bit of string matching)
12. UVa 12346 - Water Gate Management (LA 5723, Phuket11, try all 2n com-
binations, pick the best one)
13. UVa 12348 - Fun Coloring (LA 5725, Phuket11, try all 2n combinations)
14. UVa 12406 - Help Dexter (try all 2p possible bitmasks, change ‘0’s to ‘2’s)
15. UVa 12455 - Bars * (discussed in this section)
• Recursive Backtracking (Easy)
1. UVa 00167 - The Sultan Successor (8-queens chess problem)
2. UVa 00380 - Call Forwarding (simple backtracking, but we have to work with
strings, see Section 6.2)
3. UVa 00539 - The Settlers ... (longest simple path in a small general graph)
4. UVa 00624 - CD * (input size is small, backtracking is enough)
5. UVa 00628 - Passwords (backtracking, follow the rules in description)
6. UVa 00677 - All Walks of length “n” ... (print all solutions with backtracking)
7. UVa 00729 - The Hamming Distance ... (generate all possible bit strings)
8. UVa 00750 - 8 Queens Chess Problem (discussed in this section with sample
source code)
9. UVa 10276 - Hanoi Tower Troubles Again (insert a number one by one)
10. UVa 10344 - 23 Out of 5 (rearrange the 5 operands and the 3 operators)
11. UVa 10452 - Marcus, help (at each pos, Indy can go forth/left/right; try all)
12. UVa 10576 - Y2K Accounting Bug * (generate all, prune, take max)
13. UVa 11085 - Back to the 8-Queens * (see UVa 750, pre-calculation)
• Recursive Backtracking (Medium)
1. UVa 00222 - Budget Travel (looks like a DP problem, but the state cannot
be memoized as ‘tank’ is floating-point; fortunately, the input is not large)
2. UVa 00301 - Transportation (222 with pruning is possible)
3. UVa 00331 - Mapping the Swaps (n ≤ 5...)
4. UVa 00487 - Boggle Blitz (use map to store the generated words)
5. UVa 00524 - Prime Ring Problem * (also see Section 5.5.1)

82
CHAPTER 3. PROBLEM SOLVING PARADIGMS 
c Steven & Felix

6. UVa 00571 - Jugs (solution can be suboptimal, add flag to avoid cycling)
7. UVa 00574 - Sum It Up * (print all solutions with backtracking)
8. UVa 00598 - Bundling Newspaper (print all solutions with backtracking)
9. UVa 00775 - Hamiltonian Cycle (backtracking suffices because the search
space cannot be that big; in a dense graph, it is more likely to have a Hamil-
tonian cycle, so we can prune early; we do NOT have to find the best one
like in TSP problem)
10. UVa 10001 - Garden of Eden (the upperbound of 232 is scary but with
efficient pruning, we can pass the time limit as the test case is not extreme)
11. UVa 10063 - Knuth’s Permutation (do as asked)
12. UVa 10460 - Find the Permuted String (similar nature with UVa 10063)
13. UVa 10475 - Help the Leaders (generate and prune; try all)
14. UVa 10503 - The dominoes solitaire * (max 13 spaces only)
15. UVa 10506 - Ouroboros (any valid solution is AC; generate all possible next
digit (up to base 10/digit [0..9]); check if it is still a valid Ouroboros sequence)
16. UVa 10950 - Bad Code (sort the input; run backtracking; the output should
be sorted; only display the first 100 sorted output)
17. UVa 11201 - The Problem with the ... (backtracking involving strings)
18. UVa 11961 - DNA (there are at most 410 possible DNA strings; moreover,
the mutation power is at most K ≤ 5 so the search space is much smaller;
sort the output and then remove duplicates)
• Recursive Backtracking (Harder)
1. UVa 00129 - Krypton Factor (backtracking, string processing check, a bit of
output formatting)
2. UVa 00165 - Stamps (requires some DP too; can be pre-calculated)
3. UVa 00193 - Graph Coloring * (Max Independent Set, input is small)
4. UVa 00208 - Firetruck (backtracking with some pruning)
5. UVa 00416 - LED Test * (backtrack, try all)
6. UVa 00433 - Bank (Not Quite O.C.R.) (similar to UVa 416)
7. UVa 00565 - Pizza Anyone? (backtracking with lots of pruning)
8. UVa 00861 - Little Bishops (backtracking with pruning as in 8-queens recur-
sive backtracking solution; then pre-calculate the results)
9. UVa 00868 - Numerical maze (try row 1 to N; 4 ways; some constraints)
10. UVa 01262 - Password * (LA 4845, Daejeon10, sort the columns in the
two 6×5 grids first so that we can process common passwords in lexicographic
order; backtracking; important: skip two similar passwords)
11. UVa 10094 - Place the Guards (this problem is like the n-queens chess prob-
lem, but must find/use the pattern!)
12. UVa 10128 - Queue (backtracking with pruning; try up to all N! (13!) per-
mutations that satisfy the requirement; then pre-calculate the results)
13. UVa 10582 - ASCII Labyrinth (simplify complex input first; then backtrack)
14. UVa 11090 - Going in Cycle (minimum mean weight cycle problem; solvable
with backtracking with important pruning when current running mean is
greater than the best found mean weight cycle cost)

83
3.3. DIVIDE AND CONQUER 
c Steven & Felix

3.3 Divide and Conquer


Divide and Conquer (abbreviated as D&C) is a problem-solving paradigm in which a problem
is made simpler by ‘dividing’ it into smaller parts and then conquering each part. The steps:

1. Divide the original problem into sub-problems—usually by half or nearly half,


2. Find (sub)-solutions for each of these sub-problems—which are now easier,
3. If needed, combine the sub-solutions to get a complete solution for the main problem.

We have seen examples of the D&C paradigm in the previous sections of this book: Various
sorting algorithms (e.g. Quick Sort, Merge Sort, Heap Sort) and Binary Search in Section
2.2 utilize this paradigm. The way data is organized in Binary Search Tree, Heap, Segment
Tree, and Fenwick Tree in Section 2.3, 2.4.3, and 2.4.4 also relies upon the D&C paradigm.

3.3.1 Interesting Usages of Binary Search


In this section, we discuss the D&C paradigm in the well-known Binary Search algorithm.
We classify Binary Search as a ‘Divide’ and Conquer algorithm although one reference [40]
suggests that it should be actually classified as ‘Decrease (by-half)’ and Conquer as it does
not actually ‘combine’ the result. We highlight this algorithm because many contestants
know it, but not many are aware that it can be used in many other non-obvious ways.

Binary Search: The Ordinary Usage


Recall that the canonical usage of Binary Search is searching for an item in a static sorted
array. We check the middle of the sorted array to determine if it contains what we are
looking for. If it is or there are no more items to consider, stop. Otherwise, we can decide
whether the answer is to the left or right of the middle element and continue searching.
As the size of search space is halved (in a binary fashion) after each check, the complexity
of this algorithm is O(log n). In Section 2.2, we have seen that there are built-in library
routines for this algorithm, e.g. the C++ STL algorithm::lower bound (and the Java
Collections.binarySearch).
This is not the only way to use binary search. The pre-requisite for performing a binary
search—a static sorted sequence (array or vector)—can also be found in other uncommon
data structures such as in the root-to-leaf path of a tree (not necessarily binary nor complete)
that satisfies the min heap property. This variant is discussed below.

Binary Search on Uncommon Data Structures


This original problem is titled ‘My Ancestor’ and was used in the Thailand ICPC National
Contest 2009. Abridged problem description: Given a weighted (family) tree of up to N ≤
80K vertices with a special trait: Vertex values are increasing from root to leaves. Find
the ancestor vertex closest to the root from a starting vertex v that has weight at least P .
There are up to Q ≤ 20K such offline queries. Examine Figure 3.3 (left). If P = 4, then
the answer is the vertex labeled with ‘B’ with value 5 as it is the ancestor of vertex v that
is closest to root ‘A’ and has a value of ≥ 4. If P = 7, then the answer is ‘C’, with value 7.
If P ≥ 9, there is no answer.
The naı̈ve solution is to perform a linear O(N) scan per query: Starting from the given
vertex v, we move up the (family) tree until we reach the first vertex whose direct parent
has value < P or until we reach the root. If this vertex has value ≥ P and it is not vertex v

84
CHAPTER 3. PROBLEM SOLVING PARADIGMS 
c Steven & Felix

Figure 3.3: My Ancestor (all 5 root-to-leaf paths are sorted)

itself, we have found the solution. As there are Q queries, this approach runs in O(QN) (the
input tree can be a sorted linked list, or rope, of length N) and will get a TLE as N ≤ 80K
and Q ≤ 20K.
A better solution is to store all the 20K queries (we do not have to answer them im-
mediately). Traverse the tree just once starting from the root using the O(N) preorder
tree traversal algorithm (Section 4.7.2). This preorder tree traversal is slightly modified to
remember the partial root-to-current-vertex sequence as it executes. The array is always
sorted because the vertices along the root-to-current-vertex path have increasing weights,
see Figure 3.3 (right). The preorder tree traversal on the tree shown in Figure 3.3 (left)
produces the following partial root-to-current-vertex sorted array: {{3}, {3, 5}, {3, 5, 7},
{3, 5, 7, 8}, backtrack, {3, 5, 7, 9}, backtrack, backtrack, backtrack, {3, 8}, backtrack,
{3, 6}, {3, 6, 20}, backtrack, {3, 6, 10}, and finally {3, 6, 10, 20}, backtrack, backtrack,
backtrack (done)}.
During the preorder traversal, when we land on a queried vertex, we can perform a
O(log N) binary search (to be precise: lower bound) on the partial root-to-current-vertex
weight array to obtain the ancestor closest to the root with a value of at least P , recording
these solutions. Finally, we can perform a simple O(Q) iteration to output the results. The
overall time complexity of this approach is O(Q log N), which is now manageable given the
input bounds.

Bisection Method
We have discussed the applications of Binary Searches in finding items in static sorted
sequences. However, the binary search principle4 can also be used to find the root of a
function that may be difficult to compute directly.
Example: You buy a car with loan and now want to pay the loan in monthly installments
of d dollars for m months. Suppose the value of the car is originally v dollars and the bank
charges an interest rate of i% for any unpaid loan at the end of each month. What is the
amount of money d that you must pay per month (to 2 digits after the decimal point)?
Suppose d = 576.19, m = 2, v = 1000, and i = 10%. After one month, your debt
becomes 1000 × (1.1) − 576.19 = 523.81. After two months, your debt becomes 523.81 ×
(1.1) − 576.19 ≈ 0. If we are only given m = 2, v = 1000, and i = 10%, how would we
determine that d = 576.19? In other words, find the root d such that the debt payment
function f (d, m, v, i) ≈ 0.
An easy way to solve this root finding problem is to use the bisection method. We pick
a reasonable range as a starting point. We want to fix d within the range [a..b] where
4
We use the term ‘binary search principle’ to refer to the D&C approach of halving the range of possible
answers. The ‘binary search algorithm’ (finding index of an item in a sorted array), the ‘bisection method’
(finding the root of a function), and ‘binary search the answer’ (discussed in the next subsection) are all
instances of this principle.

85
3.3. DIVIDE AND CONQUER 
c Steven & Felix

a = 0.01 as we have to pay at least one cent and b = (1 + i%) × v as the earliest we can
complete the payment is m = 1 if we pay exactly (1 + i%) × v dollars after one month. In
this example, b = (1 + 0.1) × 1000 = 1100.00 dollars. For the bisection method to work5 ,
we must ensure that the function values of the two extreme points in the initial Real range
[a..b], i.e. f (a) and f (b) have opposite signs (this is true for the computed a and b above).

a b d = a+b
2
status: f (d, m, v, i) action
0.01 1100.00 550.005 undershoot by 54.9895 increase d
550.005 1100.00 825.0025 overshoot by 522.50525 decrease d
550.005 825.0025 687.50375 overshoot by 233.757875 decrease d
550.005 687.50375 618.754375 overshoot by 89.384187 decrease d
550.005 618.754375 584.379688 overshoot by 17.197344 decrease d
550.005 584.379688 567.192344 undershoot by 18.896078 increase d
567.192344 584.379688 575.786016 undershoot by 0.849366 increase d
... ... ... a few iterations later . . . ...
... ... 576.190476 stop; error is now less than  answer = 576.19

Table 3.1: Running Bisection Method on the Example Function

Notice that bisection method only requires O(log2 ((b − a)/)) iterations to get an answer
that is good enough (the error is smaller than the threshold error  that we can tolerate).
In this example, bisection method only takes log2 1099.99/ tries. Using a small  = 1e-9,
this yields only ≈ 40 iterations. Even if we use a smaller  = 1e-15, we will still only need
≈ 60 tries. Notice that the number of tries is small. The bisection method is much more
efficient compared to exhaustively evaluating each possible value of d =[0.01..1100.00]/
for this example function. Note: The bisection method can be written with a loop that tries
the values of d ≈ 40 to 60 times (see our implementation in the ‘binary search the answer’
discussion below).

Binary Search the Answer


The abridged version of UVa 11935 - Through the Desert is as follows: Imagine that you are
an explorer trying to cross a desert. You use a jeep with a ‘large enough’ fuel tank – initially
full. You encounter a series of events throughout your journey such as ‘drive (that consumes
fuel)’, ‘experience gas leak (further reduces the amount of fuel left)’, ‘encounter gas station
(allowing you to refuel to the original capacity of your jeep’s fuel tank)’, ‘encounter mechanic
(fixes all leaks)’, or ‘reach goal (done)’. You need to determine the smallest possible fuel
tank capacity for your jeep to be able to reach the goal. The answer must be precise to three
digits after decimal point.
If we know the jeep’s fuel tank capacity, then this problem is just a simulation problem.
From the start, we can simulate each event in order and determine if the goal can be reached
without running out of fuel. The problem is that we do not know the jeep’s fuel tank
capacity—this is the value that we are looking for.
From the problem description, we can compute that the range of possible answers is
between [0.000..10000.000], with 3 digits of precision. However, there are 10M such
possibilities. Trying each value sequentially will get us a TLE verdict.
Fortunately, this problem has a property that we can exploit. Suppose that the correct
answer is X. Setting your jeep’s fuel tank capacity to any value between [0.000..X-0.001]
5
Note that the requirements for the bisection method (which uses the binary search principle) are slightly
different from the binary search algorithm which needs a sorted array.

86
CHAPTER 3. PROBLEM SOLVING PARADIGMS 
c Steven & Felix

will not bring your jeep safely to the goal event. On the other hand, setting your jeep fuel
tank volume to any value between [X..10000.000] will bring your jeep safely to the goal
event, usually with some fuel left. This property allows us to binary search the answer X!
We can use the following code to obtain the solution for this problem.

#define EPS 1e-9 // this value is adjustable; 1e-9 is usually small enough
bool can(double f) { // details of this simulation is omitted
// return true if the jeep can reach goal state with fuel tank capacity f
// return false otherwise
}

// inside int main()


// binary search the answer, then simulate
double lo = 0.0, hi = 10000.0, mid = 0.0, ans = 0.0;
while (fabs(hi - lo) > EPS) { // when the answer is not found yet
mid = (lo + hi) / 2.0; // try the middle value
if (can(mid)) { ans = mid; hi = mid; } // save the value, then continue
else lo = mid;
}

printf("%.3lf\n", ans); // after the loop is over, we have the answer

Note that some programmers choose to use a constant number of refinement iterations
instead of allowing the number of iterations to vary dynamically to avoid precision errors
when testing fabs(hi - lo) > EPS and thus being trapped in an infinite loop. The only
changes required to implement this approach are shown below. The other parts of the code
are the same as above.

double lo = 0.0, hi = 10000.0, mid = 0.0, ans = 0.0;


for (int i = 0; i < 50; i++) { // log_2 ((10000.0 - 0.0) / 1e-9) ~= 43
mid = (lo + hi) / 2.0; // looping 50 times should be precise enough
if (can(mid)) { ans = mid; hi = mid; }
else lo = mid;
}

Exercise 3.3.1.1: There is an alternative solution for UVa 11935 that does not use ‘binary
search the answer’ technique. Can you spot it?
Exercise 3.3.1.2*: The example shown here involves binary-searching the answer where
the answer is a floating point number. Modify the code to solve ‘binary search the answer’
problems where the answer lies in an integer range!

Remarks About Divide and Conquer in Programming Contests


The Divide and Conquer paradigm is usually utilized through popular algorithms that rely
on it: Binary Search and its variants, Merge/Quick/Heap Sort, and data structures: Binary
Search Tree, Heap, Segment Tree, Fenwick Tree, etc. However—based on our experience,
we reckon that the most commonly used form of the Divide and Conquer paradigm in

87
3.3. DIVIDE AND CONQUER 
c Steven & Felix

programming contests is the Binary Search principle. If you want to do well in programming
contests, please spend time practicing the various ways to apply it.
Once you are more familiar with the ‘Binary Search the Answer’ technique discussed in
this section, please explore Section 8.4.1 for a few more programming exercises that use this
technique with other algorithm that we will discuss in the latter parts of this book.
We notice that there are not that many D&C problems outside of our binary search
categorization. Most D&C solutions are ‘geometry-related’ or ‘problem specific’, and thus
cannot be discussed in detail in this book. However, we will encounter some of them in
Section 8.4.1 (binary search the answer plus geometry formulas), Section 9.14 (Inversion
Index), Section 9.21 (Matrix Power), and Section 9.29 (Selection Problem).

Programming Exercises solvable using Divide and Conquer:


• Binary Search
1. UVa 00679 - Dropping Balls (binary search; bit manipulation solutions exist)
2. UVa 00957 - Popes (complete search + binary search: upper bound)
3. UVa 10077 - The Stern-Brocot ... (binary search)
4. UVa 10474 - Where is the Marble? (simple: use sort and then lower bound)
5. UVa 10567 - Helping Fill Bates * (store increasing indices of each char
of ‘S’ in 52 vectors; for each query, binary search for the position of the char
in the correct vector)
6. UVa 10611 - Playboy Chimp (binary search)
7. UVa 10706 - Number Sequence (binary search + some mathematical insights)
8. UVa 10742 - New Rule in Euphomia (use sieve; binary search)
9. UVa 11057 - Exact Sum * (sort, for price p[i], check if price (M - p[i])
exists with binary search)
10. UVa 11621 - Small Factors (generate numbers with factor 2 and/or 3, sort,
upper bound)
11. UVa 11701 - Cantor (a kind of ternary search)
12. UVa 11876 - N + NOD (N) ([lower|upper] bound on sorted sequence N)
13. UVa 12192 - Grapevine * (the input array has special sorted properties;
use lower bound to speed up the search)
14. Thailand ICPC National Contest 2009 - My Ancestor (author: Felix Halim)
• Bisection Method or Binary Search the Answer
1. UVa 10341 - Solve It * (bisection method discussed in this section; for al-
ternative solutions, see http://www.algorithmist.com/index.php/UVa 10341)
2. UVa 11413 - Fill the ... * (binary search the answer + simulation)
3. UVa 11881 - Internal Rate of Return (bisection method)
4. UVa 11935 - Through the Desert (binary search the answer + simulation)
5. UVa 12032 - The Monkey ... * (binary search the answer + simulation)
6. UVa 12190 - Electric Bill (binary search the answer + algebra)
7. IOI 2010 - Quality of Living (binary search the answer)
Also see: Divide & Conquer for Geometry Problems (see Section 8.4.1)
• Other Divide & Conquer Problems
1. UVa 00183 - Bit Maps * (simple exercise of Divide and Conquer)
2. IOI 2011 - Race (D&C; whether the solution path uses a vertex or not)
Also see: Data Structures with Divide & Conquer flavor (see Section 2.3)

88
CHAPTER 3. PROBLEM SOLVING PARADIGMS 
c Steven & Felix

3.4 Greedy
An algorithm is said to be greedy if it makes the locally optimal choice at each step with the
hope of eventually reaching the globally optimal solution. In some cases, greedy works—the
solution is short and runs efficiently. For many others, however, it does not. As discussed
in other typical Computer Science textbooks, e.g. [7, 38], a problem must exhibit these two
properties in order for a greedy algorithm to work:
1. It has optimal sub-structures.
Optimal solution to the problem contains optimal solutions to the sub-problems.
2. It has the greedy property (difficult to prove in time-critical contest environment!).
If we make a choice that seems like the best at the moment and proceed to solve the
remaining subproblem, we reach the optimal solution. We will never have to reconsider
our previous choices.

3.4.1 Examples
Coin Change - The Greedy Version
Problem description: Given a target amount V cents and a list of denominations of n coins,
i.e. we have coinValue[i] (in cents) for coin types i ∈ [0..n-1], what is the minimum
number of coins that we must use to represent amount V ? Assume that we have an unlimited
supply of coins of any type. Example: If n = 4, coinValue = {25, 10, 5, 1} cents6 , and
we want to represent V = 42 cents, we can use this Greedy algorithm: Select the largest
coin denomination which is not greater than the remaining amount, i.e. 42-25 = 17 → 17-10
= 7 → 7-5 = 2 → 2-1 = 1 → 1-1 = 0, a total of 5 coins. This is optimal.
The problem above has the two ingredients required for a successful greedy algorithm:
1. It has optimal sub-structures.
We have seen that in our quest to represent 42 cents, we used 25+10+5+1+1.
This is an optimal 5-coin solution to the original problem!
Optimal solutions to sub-problem are contained within the 5-coin solution, i.e.
a. To represent 17 cents, we can use 10+5+1+1 (part of the solution for 42 cents),
b. To represent 7 cents, we can use 5+1+1 (also part of the solution for 42 cents), etc
2. It has the greedy property: Given every amount V , we can greedily subtract from it
the largest coin denomination which is not greater than this amount V . It can be
proven (not shown here for brevity) that using any other strategies will not lead to an
optimal solution, at least for this set of coin denominations.
However, this greedy algorithm does not work for all sets of coin denominations. Take for
example {4, 3, 1} cents. To make 6 cents with that set, a greedy algorithm would choose 3
coins {4, 1, 1} instead of the optimal solution that uses 2 coins {3, 3}. The general version
of this problem is revisited later in Section 3.5.2 (Dynamic Programming).

UVa 410 - Station Balance (Load Balancing)


Given 1 ≤ C ≤ 5 chambers which can store 0, 1, or 2 specimens, 1 ≤ S ≤ 2C specimens
and a list M of the masses of the S specimens, determine which chamber should store each
specimen in order to minimize ‘imbalance’. See Figure 3.4 for a visual explanation7 .
6
The presence of the 1-cent coin ensures that we can always make every value.
7
Since C ≤ 5 and S ≤ 10, we can actually use a Complete Search solution for this problem. However,
this problem is simpler to solve using the Greedy algorithm.

89
3.4. GREEDY 
c Steven & Felix

S
A=( j=1 Mj )/C, i.e. A is the average of the total mass in each of the C chambers.
C
Imbalance = i=1 |Xi − A|, i.e. the sum of differences between the total mass in each
chamber w.r.t. A where Xi is the total mass of specimens in chamber i.

Figure 3.4: Visualization of UVa 410 - Station Balance


This problem can be solved using a greedy algorithm, but to arrive at that solution, we have
to make several observations.

Figure 3.5: UVa 410 - Observations


Observation 1: If there exists an empty chamber, it is usually beneficial and never worse to
move one specimen from a chamber with two specimens to the empty chamber! Otherwise,
the empty chamber contributes more to the imbalance as shown in Figure 3.5, top.
Observation 2: If S > C, then S − C specimens must be paired with a chamber already
containing other specimens—the Pigeonhole principle! See Figure 3.5, bottom.
The key insight is that the solution to this problem can be simplified with sorting:
if S < 2C, add 2C − S dummy specimens with mass 0. For example, C = 3, S = 4,
M = {5, 1, 2, 7} → C = 3, S = 6, M = {5, 1, 2, 7, 0, 0}. Then, sort the specimens on their
mass such that M1 ≤ M2 ≤ . . . ≤ M2C−1 ≤ M2C . In this example, M = {5, 1, 2, 7, 0, 0} →
{0, 0, 1, 2, 5, 7}. By adding dummy specimens and then sorting them, a greedy strategy
becomes ‘apparent’:
• Pair the specimens with masses M1 &M2C and put them in chamber 1, then
• Pair the specimens with masses M2 &M2C−1 and put them in chamber 2, and so on . . .
This greedy algorithm—known as load balancing—works! See Figure 3.6.
It is hard to impart the techniques used in deriving this greedy solution. Finding greedy
solutions is an art, just as finding good Complete Search solutions requires creativity. A tip
that arises from this example: If there is no obvious greedy strategy, try sorting the data or
introducing some tweak and see if a greedy strategy emerges.

90
CHAPTER 3. PROBLEM SOLVING PARADIGMS 
c Steven & Felix

Figure 3.6: UVa 410 - Greedy Solution

UVa 10382 - Watering Grass (Interval Covering)

Problem description: n sprinklers are installed in a horizontal strip of grass L meters long
and W meters wide. Each sprinkler is centered vertically in the strip. For each sprinkler,
we are given its position as the distance from the left end of the center line and its radius of
operation. What is the minimum number of sprinklers that should be turned on in order to
water the entire strip of grass? Constraint: n ≤ 10000. For an illustration of the problem,
see Figure 3.7—left side. The answer for this test case is 6 sprinklers (those labeled with
{A, B, D, E, F, H}). There are 2 unused sprinklers: {C, G}.
We cannot solve this problem with a brute force strategy that tries all possible subsets of
sprinklers to be turned on since the number of sprinklers can go up to 10000. It is definitely
infeasible to try all 210000 possible subsets of sprinklers.
This problem is actually a variant of the well known greedy problem called the interval
covering problem. However, it includes a simple geometric twist. The original interval
covering problem deals with intervals. This problem deals with sprinklers that have circles
of influence in a horizontal area rather than simple intervals. We first have to transform the
problem to resemble the standard interval covering problem.
See Figure 3.7—right side. We can convert these circles and horizontal strips into inter-
vals. We can compute dx = sqrt(R2 - (W/2)2). Suppose a circle is centered at (x, y).
The interval represented by this circle is [x-dx..x+dx]. To see why this works, notice that
the additional circle segment beyond dx away from x does not completely cover the strip in
the horizontal region it spans. If you have difficulties with this geometric transformation,
see Section 7.2.4 which discusses basic operations involving a right triangle.

Figure 3.7: UVa 10382 - Watering Grass

91
3.4. GREEDY 
c Steven & Felix

Now that we have transformed the original problem into the interval covering problem, we
can use the following Greedy algorithm. First, the Greedy algorithm sorts the intervals by
increasing left endpoint and by decreasing right endpoint if ties arise. Then, the Greedy
algorithm processes the intervals one at a time. It takes the interval that covers ‘as far
right as possible’ and yet still produces uninterrupted coverage from the leftmost side to the
rightmost side of the horizontal strip of grass. It ignores intervals that are already completely
covered by other (previous) intervals.
For the test case shown in Figure 3.7—left side, this Greedy algorithm first sorts the
intervals to obtain the sequence {A, B, C, D, E, F, G, H}. Then it processes them one by
one. First, it takes ‘A’ (it has to), takes ‘B’ (connected to interval ‘A’), ignores ‘C’ (as it is
embedded inside interval ‘B’), takes ‘D’ (it has to, as intervals ‘B’ and ‘E’ are not connected
if ‘D’ is not used), takes ‘E’, takes ‘F’, ignores ‘G’ (as taking ‘G’ is not ‘as far right as
possible’ and does not reach the rightmost side of the grass strip), takes ‘H’ (as it connects
with interval ‘F’ and covers more to the right than interval of ‘G’ does, going beyond the
rightmost end of the grass strip). In total, we select 6 sprinklers: {A, B, D, E, F, H}. This
is the minimum possible number of sprinklers for this test case.

UVa 11292 - Dragon of Loowater (Sort the Input First)


Problem description: There are n dragon heads and m knights (1 ≤ n, m ≤ 20000). Each
dragon head has a diameter and each knight has a height. A dragon head with diameter
D can be chopped off by a knight with height H if D ≤ H. A knight can only chop off
one dragon head. Given a list of diameters of the dragon heads and a list of heights of the
knights, is it possible to chop off all the dragon heads? If yes, what is the minimum total
height of the knights used to chop off the dragons’ heads?
There are several ways to solve this problem, but we will illustrate one that is probably
the easiest. This problem is a bipartite matching problem (this will be discussed in more
detail in Section 4.7.4), in the sense that we are required to match (pair) certain knights
to dragon heads in a maximal fashion. However, this problem can be solved greedily: Each
dragon head should be chopped by a knight with the shortest height that is at least as tall
as the diameter of the dragon’s head. However, the input is given in an arbitrary order. If
we sort both the list of dragon head diameters and knight heights in O(n log n + m log m),
we can use the following O(min(n, m)) scan to determine the answer. This is yet another
example where sorting the input can help produce the required greedy strategy.

gold = d = k = 0; // array dragon+knight are sorted in non decreasing order


while (d < n && k < m) { // still have dragon heads or knights
while (dragon[d] > knight[k] && k < m) k++; // find the required knight
if (k == m) break; // no knight can kill this dragon head, doomed :S
gold += knight[k]; // the king pay this amount of gold
d++; k++; // next dragon head and knight please
}

if (d == n) printf("%d\n", gold); // all dragon heads are chopped


else printf("Loowater is doomed!\n");

Exercise 3.4.1.1*: Which of the following sets of coins (all in cents) are solvable using the
greedy ‘coin change’ algorithm discussed in this section? If the greedy algorithm fails on a
certain set of coin denominations, determine the smallest counter example V cents on which
it fails to be optimal. See [51] for more details about finding such counter examples.

92
CHAPTER 3. PROBLEM SOLVING PARADIGMS 
c Steven & Felix

1. S1 = {10, 7, 5, 4, 1}
2. S2 = {64, 32, 16, 8, 4, 2, 1}
3. S3 = {13, 11, 7, 5, 3, 2, 1}
4. S4 = {7, 6, 5, 4, 3, 2, 1}
5. S5 = {21, 17, 11, 10, 1}

Remarks About Greedy Algorithm in Programming Contests


In this section, we have discussed three classical problems solvable with Greedy algorithms:
Coin Change (the special case), Load Balancing, and Interval Covering. For these classical
problems, it is helpful to memorize their solutions (for this case, ignore that we have said
earlier in the chapter about not relying too much on memorization). We have also discussed
an important problem solving strategy usually applicable to greedy problems: Sorting the
input data to elucidate hidden greedy strategies.
There are two other classical examples of Greedy algorithms in this book, e.g. Kruskal’s
(and Prim’s) algorithm for the Minimum Spanning Tree (MST) problem (see Section 4.3)
and Dijkstra’s algorithm for the Single-Source Shortest Paths (SSSP) problem (see Section
4.4.3). There are many more known Greedy algorithms that we have chosen not to discuss
in this book as they are too ‘problem specific’ and rarely appear in programming contests,
e.g. Huffman Codes [7, 38], Fractional Knapsack [7, 38], some Job Scheduling problems, etc.
However, today’s programming contests (both ICPC and IOI) rarely involve the purely
canonical versions of these classical problems. Using Greedy algorithms to attack a ‘non
classical’ problem is usually risky. A Greedy algorithm will normally not encounter the TLE
response as it is often lightweight, but instead tends to obtain WA verdicts. Proving that a
certain ‘non-classical’ problem has optimal sub-structure and greedy property during contest
time may be difficult or time consuming, so a competitive programmer should usually use
this rule of thumb:
If the input size is ‘small enough’ to accommodate the time complexity of either Complete
Search or Dynamic Programming approaches (see Section 3.5), then use these approaches
as both will ensure a correct answer. Only use a Greedy algorithm if the input size given in
the problem statement are too large even for the best Complete Search or DP algorithm.
Having said that, it is increasingly true that problem authors try to set the input bounds
of problems that allow for Greedy strategies to be in an ambiguous range so that contestants
cannot use the input size to quickly determine the required algorithm!
We have to remark that it is quite challenging to come up with new ‘non-classical’
Greedy problems. Therefore, the number of such novel Greedy problems used in competitive
programming is lower than that of Complete Search or Dynamic Programming problems.

Programming Exercises solvable using Greedy


(most hints are omitted to keep the problems challenging):
• Classical, Usually Easier
1. UVa 00410 - Station Balance (discussed in this section, load balancing)
2. UVa 01193 - Radar Installation (LA 2519, Beijing02, interval covering)
3. UVa 10020 - Minimal Coverage (interval covering)
4. UVa 10382 - Watering Grass (discussed in this section, interval covering)
5. UVa 11264 - Coin Collector * (coin change variant)

93
3.4. GREEDY 
c Steven & Felix

6. UVa 11389 - The Bus Driver Problem * (load balancing)


7. UVa 12321 - Gas Station (interval covering)
8. UVa 12405 - Scarecrow * (simpler interval covering problem)
9. IOI 2011 - Elephants (optimized greedy solution can be used up to subtask 3,
but the harder subtasks 4 and 5 must be solved using efficient data structure)
• Involving Sorting (Or The Input Is Already Sorted)
1. UVa 10026 - Shoemaker’s Problem
2. UVa 10037 - Bridge
3. UVa 10249 - The Grand Dinner
4. UVa 10670 - Work Reduction
5. UVa 10763 - Foreign Exchange
6. UVa 10785 - The Mad Numerologist
7. UVa 11100 - The Trip, 2007 *
8. UVa 11103 - WFF’N Proof
9. UVa 11269 - Setting Problems
10. UVa 11292 - Dragon of Loowater *
11. UVa 11369 - Shopaholic
12. UVa 11729 - Commando War
13. UVa 11900 - Boiled Eggs
14. UVa 12210 - A Match Making Problem *
15. UVa 12485 - Perfect Choir
• Non Classical, Usually Harder
1. UVa 00311 - Packets
2. UVa 00668 - Parliament
3. UVa 10152 - ShellSort
4. UVa 10340 - All in All
5. UVa 10440 - Ferry Loading II
6. UVa 10602 - Editor Nottobad
7. UVa 10656 - Maximum Sum (II) *
8. UVa 10672 - Marbles on a tree
9. UVa 10700 - Camel Trading
10. UVa 10714 - Ants
11. UVa 10718 - Bit Mask *
12. UVa 10982 - Troublemakers
13. UVa 11054 - Wine Trading in Gergovia
14. UVa 11157 - Dynamic Frog *
15. UVa 11230 - Annoying painting tool
16. UVa 11240 - Antimonotonicity
17. UVa 11335 - Discrete Pursuit
18. UVa 11520 - Fill the Square
19. UVa 11532 - Simple Adjacency ...
20. UVa 11567 - Moliu Number Generator
21. UVa 12482 - Short Story Competition

94
CHAPTER 3. PROBLEM SOLVING PARADIGMS 
c Steven & Felix

3.5 Dynamic Programming


Dynamic Programming (from now on abbreviated as DP) is perhaps the most challenging
problem-solving technique among the four paradigms discussed in this chapter. Thus, make
sure that you have mastered the material mentioned in the previous chapters/sections before
reading this section. Also, prepare to see lots of recursion and recurrence relations!
The key skills that you have to develop in order to master DP are the abilities to deter-
mine the problem states and to determine the relationships or transitions between current
problems and their sub-problems. We have used these skills earlier in recursive backtracking
(see Section 3.2.2). In fact, DP problems with small input size constraints may already be
solvable with recursive backtracking.
If you are new to DP technique, you can start by assuming that (the ‘top-down’) DP is
a kind of ‘intelligent’ or ‘faster’ recursive backtracking. In this section, we will explain the
reasons why DP is often faster than recursive backtracking for problems amenable to it.
DP is primarily used to solve optimization problems and counting problems. If you
encounter a problem that says “minimize this” or “maximize that” or “count the ways to
do that”, then there is a (high) chance that it is a DP problem. Most DP problems in
programming contests only ask for the optimal/total value and not the optimal solution
itself, which often makes the problem easier to solve by removing the need to backtrack and
produce the solution. However, some harder DP problems also require the optimal solution
to be returned in some fashion. We will continually refine our understanding of Dynamic
Programming in this section.

3.5.1 DP Illustration
We will illustrate the concept of Dynamic Programming with an example problem: UVa
11450 - Wedding Shopping. The abridged problem statement: Given different options for
each garment (e.g. 3 shirt models, 2 belt models, 4 shoe models, . . . ) and a certain limited
budget, our task is to buy one model of each garment. We cannot spend more money than
the given budget, but we want to spend the maximum possible amount.
The input consists of two integers 1 ≤ M ≤ 200 and 1 ≤ C ≤ 20, where M is the budget
and C is the number of garments that you have to buy, followed by some information about
the C garments. For the garment g ∈ [0..C-1], we will receive an integer 1 ≤ K ≤ 20
which indicates the number of different models there are for that garment g, followed by K
integers indicating the price of each model ∈ [1..K] of that garment g.
The output is one integer that indicates the maximum amount of money we can spend
purchasing one of each garment without exceeding the budget. If there is no solution due to
the small budget given to us, then simply print “no solution”.
Suppose we have the following test case A with M = 20, C = 3:
Price of the 3 models of garment g = 0 → 6 4 8 // the prices are not sorted in the input
Price of the 2 models of garment g = 1 → 5 10
Price of the 4 models of garment g = 2→1535
For this test case, the answer is 19, which may result from buying the underlined items
(8+10+1). This is not unique, as solutions (6+10+3) and (4+10+5) are also optimal.
However, suppose we have this test case B with M = 9 (limited budget), C = 3:
Price of the 3 models of garment g = 0→648
Price of the 2 models of garment g = 1 → 5 10
Price of the 4 models of garment g = 2→1535

95
3.5. DYNAMIC PROGRAMMING 
c Steven & Felix

The answer is then “no solution” because even if we buy all the cheapest models for each
garment, the total price (4+5+1) = 10 still exceeds our given budget M = 9.
In order for us to appreciate the usefulness of Dynamic Programming in solving the
above-mentioned problem, let’s explore how far the other approaches discussed earlier will
get us in this particular problem.

Approach 1: Greedy (Wrong Answer)


Since we want to maximize the budget spent, one greedy idea (there are other greedy
approaches—which are also WA) is to take the most expensive model for each garment
g which still fits our budget. For example in test case A above, we can choose the most
expensive model 3 of garment g = 0 with price 8 (money is now 20-8 = 12), then choose
the most expensive model 2 of garment g = 1 with price 10 (money = 12-10 = 2), and
finally for the last garment g = 2, we can only choose model 1 with price 1 as the money we
have left does not allow us to buy the other models with price 3 or 5. This greedy strategy
‘works’ for test cases A and B above and produce the same optimal solution (8+10+1) = 19
and “no solution”, respectively. It also runs very fast8 : 20 + 20 + . . . + 20 for a total of 20
times = 400 operations in the worst case. However, this greedy strategy does not work for
many other test cases, such as this counter-example below (test case C):
Test case C with M = 12, C = 3:
3 models of garment g = 0 → 6 4 8
2 models of garment g = 1 → 5 10
4 models of garment g = 2 → 1 5 3 5
The Greedy strategy selects model 3 of garment g = 0 with price 8 (money = 12-8 = 4),
causing us to not have enough money to buy any model in garment g = 1, thus incorrectly
reporting “no solution”. One optimal solution is 4+5+3 = 12, which uses up all of our
budget. The optimal solution is not unique as 6+5+1 = 12 also depletes the budget.

Approach 2: Divide and Conquer (Wrong Answer)


This problem is not solvable using the Divide and Conquer paradigm. This is because the
sub-problems (explained in the Complete Search sub-section below) are not independent.
Therefore, we cannot solve them separately with the Divide and Conquer approach.

Approach 3: Complete Search (Time Limit Exceeded)


Next, let’s see if Complete Search (recursive backtracking) can solve this problem. One way
to use recursive backtracking in this problem is to write a function shop(money, g) with
two parameters: The current money that we have and the current garment g that we are
dealing with. The pair (money, g) is the state of this problem. Note that the order of
parameters does not matter, e.g. (g, money) is also a perfectly valid state. Later in Section
3.5.3, we will see more discussion on how to select appropriate states for a problem.
We start with money = M and garment g = 0. Then, we try all possible models in
garment g = 0 (a maximum of 20 models). If model i is chosen, we subtract model i’s price
from money, then repeat the process in a recursive fashion with garment g = 1 (which can
also have up to 20 models), etc. We stop when the model for the last garment g = C-1 has
been chosen. If money < 0 before we choose a model from garment g = C-1, we can prune
the infeasible solution. Among all valid combinations, we can then pick the one that results
in the smallest non-negative money. This maximizes the money spent, which is (M - money).
8
We do not need to sort the prices just to find the model with the maximum price as there are only up
to K ≤ 20 models. An O(K) scan is enough.

96
CHAPTER 3. PROBLEM SOLVING PARADIGMS 
c Steven & Felix

We can formally define these Complete Search recurrences (transitions) as follows:


1. If money < 0 (i.e. money goes negative),
shop(money, g) = −∞ (in practice, we can just return a large negative value)
2. If a model from the last garment has been bought, that is, g = C,
shop(money, g) = M - money (this is the actual money that we spent)
3. In general case, ∀ model ∈ [1..K] of current garment g,
shop(money, g) = max(shop(money - price[g][model], g + 1))
We want to maximize this value (Recall that the invalid ones have large negative value)

This solution works correctly, but it is very slow! Let’s analyze the worst case time com-
plexity. In the largest test case, garment g = 0 has up to 20 models; garment g = 1 also
has up to 20 models and all garments including the last garment g = 19 also have up to 20
models. Therefore, this Complete Search runs in 20 × 20 × . . . × 20 operations in the worst
case, i.e. 2020 = a very large number. If we can only come up with this Complete Search
solution, we cannot solve this problem.

Approach 4: Top-Down DP (Accepted)


To solve this problem, we have to use the DP concept as this problem satisfies the two
prerequisites for DP to be applicable:
1. This problem has optimal sub-structures9 .
This is illustrated in the third Complete Search recurrence above: The solution for
the sub-problem is part of the solution of the original problem. In other words, if we
select model i for garment g = 0, for our final selection to be optimal, our choice for
garments g = 1 and above must also be the optimal choice for a reduced budget of
M − price, where price refers to the price of model i.
2. This problem has overlapping sub-problems.
This is the key characteristic of DP! The search space of this problem is not as big as
the rough 2020 bound obtained earlier because many sub-problems are overlapping!

Let’s verify if this problem indeed has overlapping sub-problems. Suppose that there are 2
models in a certain garment g with the same price p. Then, a Complete Search will move to
the same sub-problem shop(money - p, g + 1) after picking either model! This situation
will also occur if some combination of money and chosen model’s price causes money1 - p1
= money2 - p2 at the same garment g. This will—in a Complete Search solution—cause the
same sub-problem to be computed more than once, an inefficient state of affairs!
So, how many distinct sub-problems (a.k.a. states in DP terminology) are there in this
problem? Only 201 × 20 = 4020. There are only 201 possible values for money (0 to 200
inclusive) and 20 possible values for the garment g (0 to 19 inclusive). Each sub-problem just
needs to be computed once. If we can ensure this, we can solve this problem much faster.
The implementation of this DP solution is surprisingly simple. If we already have the re-
cursive backtracking solution (see the recurrences—a.k.a. transitions in DP terminology—
shown in the Complete Search approach above), we can implement the top-down DP by
adding these two additional steps:
1. Initialize10 a DP ‘memo’ table with dummy values that are not used in the problem,
e.g. ‘-1’. The DP table should have dimensions corresponding to the problem states.
9
Optimal sub-structures are also required for Greedy algorithms to work, but this problem lacks the
‘greedy property’, making it unsolvable with the Greedy algorithm.
10
For C/C++ users, the memset function in <cstring> is a good tool to perform this step.

97
3.5. DYNAMIC PROGRAMMING 
c Steven & Felix

2. At the start of the recursive function, check if this state has been computed before.
(a) If it has, simply return the value from the DP memo table, O(1).
(This the origin of the term ‘memoization’.)
(b) If it has not been computed, perform the computation as per normal (only once)
and then store the computed value in the DP memo table so that further calls to
this sub-problem (state) return immediately.

Analyzing a basic11 DP solution is easy. If it has M distinct states, then it requires O(M)
memory space. If computing one state (the complexity of the DP transition) requires O(k)
steps, then the overall time complexity is O(kM). This UVa 11450 problem has M =
201 × 20 = 4020 and k = 20 (as we have to iterate through at most 20 models for each
garment g). Thus, the time complexity is at most 4020 × 20 = 80400 operations per test
case, a very manageable calculation.
We display our code below for illustration, especially for those who have never coded a
top-down DP algorithm before. Scrutinize this code and verify that it is indeed very similar
to the recursive backtracking code that you have seen in Section 3.2.

/* UVa 11450 - Wedding Shopping - Top Down */


// assume that the necessary library files have been included
// this code is similar to recursive backtracking code
// parts of the code specific to top-down DP are commented with: ‘TOP-DOWN’

int M, C, price[25][25]; // price[g (<= 20)][model (<= 20)]


int memo[210][25]; // TOP-DOWN: dp table memo[money (<= 200)][g (<= 20)]
int shop(int money, int g) {
if (money < 0) return -1000000000; // fail, return a large -ve number
if (g == C) return M - money; // we have bought last garment, done
// if the line below is commented, top-down DP will become backtracking!!
if (memo[money][g] != -1) return memo[money][g]; // TOP-DOWN: memoization
int ans = -1; // start with a -ve number as all prices are non negative
for (int model = 1; model <= price[g][0]; model++) // try all models
ans = max(ans, shop(money - price[g][model], g + 1));
return memo[money][g] = ans; } // TOP-DOWN: memoize ans and return it

int main() { // easy to code if you are already familiar with it


int i, j, TC, score;
scanf("%d", &TC);
while (TC--) {
scanf("%d %d", &M, &C);
for (i = 0; i < C; i++) {
scanf("%d", &price[i][0]); // store K in price[i][0]
for (j = 1; j <= price[i][0]; j++) scanf("%d", &price[i][j]);
}
memset(memo, -1, sizeof memo); // TOP-DOWN: initialize DP memo table
score = shop(M, 0); // start the top-down DP
if (score < 0) printf("no solution\n");
else printf("%d\n", score);
} } // return 0;
11
Basic means “without fancy optimizations that we will see later in this section and in Section 8.3”.

98
CHAPTER 3. PROBLEM SOLVING PARADIGMS 
c Steven & Felix

We want to take this opportunity to illustrate another style used in implementing DP solu-
tions (only applicable for C/C++ users). Instead of frequently addressing a certain cell in
the memo table, we can use a local reference variable to store the memory address of the
required cell in the memo table as shown below. The two coding styles are not very different,
and it is up to you to decide which style you prefer.

int shop(int money, int g) {


if (money < 0) return -1000000000; // order of >1 base cases is important
if (g == C) return M - money; // money can’t be <0 if we reach this line
int &ans = memo[money][g]; // remember the memory address
if (ans != -1) return ans;
for (int model = 1; model <= price[g][0]; model++)
ans = max(ans, shop(money - price[g][model], g + 1));
return ans; // ans (or memo[money][g]) is directly updated
}

Source code: ch3 02 UVa11450 td.cpp/java

Approach 5: Bottom-Up DP (Accepted)


There is another way to implement a DP solution often referred to as the bottom-up DP.
This is actually the ‘true form’ of DP as DP was originally known as the ‘tabular method’
(computation technique involving a table). The basic steps to build bottom-up DP solution
are as follows:

1. Determine the required set of parameters that uniquely describe the problem (the
state). This step is similar to what we have discussed in recursive backtracking and
top-down DP earlier.

2. If there are N parameters required to represent the states, prepare an N dimensional


DP table, with one entry per state. This is equivalent to the memo table in top-down
DP. However, there are differences. In bottom-up DP, we only need to initialize some
cells of the DP table with known initial values (the base cases). Recall that in top-
down DP, we initialize the memo table completely with dummy values (usually -1) to
indicate that we have not yet computed the values.

3. Now, with the base-case cells/states in the DP table already filled, determine the
cells/states that can be filled next (the transitions). Repeat this process until the DP
table is complete. For the bottom-up DP, this part is usually accomplished through
iterations, using loops (more details about this later).

For UVa 11450, we can write the bottom-up DP as follow: We describe the state of a sub-
problem with two parameters: The current garment g and the current money. This state
formulation is essentially equivalent to the state in the top-down DP above, except that we
have reversed the order to make g the first parameter (thus the values of g are the row indices
of the DP table so that we can take advantage of cache-friendly row-major traversal in a 2D
array, see the speed-up tips in Section 3.2.3). Then, we initialize a 2D table (boolean matrix)
reachable[g][money] of size 20 × 201. Initially, only cells/states reachable by buying any
of the models of the first garment g = 0 are set to true (in the first row). Let’s use test case
A above as example. In Figure 3.8, top, the only columns ‘20-6 = 14’, ‘20-4 = 16’, and ‘20-8
= 12’ in row 0 are initially set to true.

99
3.5. DYNAMIC PROGRAMMING 
c Steven & Felix

Figure 3.8: Bottom-Up DP (columns 21 to 200 are not shown)

Now, we loop from the second garment g = 1 (second row) to the last garment g = C-1 =
3-1 = 2 (third and last row) in row-major order (row by row). If reachable[g-1][money]
is true, then the next state reachable[g][money-p] where p is the price of a model of
current garment g is also reachable as long as the second parameter (money) is not negative.
See Figure 3.8, middle, where reachable[0][16] propagates to reachable[1][16-5] and
reachable[1][16-10] when the model with price 5 and 10 in garment g = 1 is bought,
respectively; reachable[0][12] propagates to reachable[1][12-10] when the model with
price 10 in garment g = 1 is bought, etc. We repeat this table filling process row by row
until we are done with the last row12 .
Finally, the answer can be found in the last row when g = C-1. Find the state in
that row that is both nearest to index 0 and reachable. In Figure 3.8, bottom, the cell
reachable[2][1] provides the answer. This means that we can reach state (money = 1)
by buying some combination of the various garment models. The required final answer is
actually M - money, or in this case, 20-1 = 19. The answer is “no solution” if there is no
state in the last row that is reachable (where reachable[C-1][money] is set to true). We
provide our implementation below for comparison with the top-down version.

/* UVa 11450 - Wedding Shopping - Bottom Up */


// assume that the necessary library files have been included

int main() {
int g, money, k, TC, M, C;
int price[25][25]; // price[g (<= 20)][model (<= 20)]
bool reachable[25][210]; // reachable table[g (<= 20)][money (<= 200)]

scanf("%d", &TC);
while (TC--) {
scanf("%d %d", &M, &C);
for (g = 0; g < C; g++) {
scanf("%d", &price[g][0]); // we store K in price[g][0]
for (money = 1; money <= price[g][0]; money++)
scanf("%d", &price[g][money]);
}
12
Later in Section 4.7.1, we will discuss DP as a traversal of an (implicit) DAG. To avoid unnecessary
‘backtracking’ along this DAG, we have to visit the vertices in their topological order (see Section 4.2.5).
The order in which we fill the DP table is a topological ordering of the underlying implicit DAG.

100
CHAPTER 3. PROBLEM SOLVING PARADIGMS 
c Steven & Felix

memset(reachable, false, sizeof reachable); // clear everything


for (g = 1; g <= price[0][0]; g++) // initial values (base cases)
if (M - price[0][g] >= 0) // to prevent array index out of bound
reachable[0][M - price[0][g]] = true; // using first garment g = 0

for (g = 1; g < C; g++) // for each remaining garment


for (money = 0; money < M; money++) if (reachable[g-1][money])
for (k = 1; k <= price[g][0]; k++) if (money - price[g][k] >= 0)
reachable[g][money - price[g][k]] = true; // also reachable now

for (money = 0; money <= M && !reachable[C - 1][money]; money++);

if (money == M + 1) printf("no solution\n"); // last row has no on bit


else printf("%d\n", M - money);
}
} // return 0;

Source code: ch3 03 UVa11450 bu.cpp/java

There is an advantage for writing DP solutions in the bottom-up fashion. For problems
where we only need the last row of the DP table (or, more generally, the last updated slice
of all the states) to determine the solution—including this problem, we can optimize the
memory usage of our DP solution by sacrificing one dimension in our DP table. For harder
DP problems with tight memory requirements, this ‘space saving trick’ may prove to be
useful, though the overall time complexity does not change.
Let’s take a look again at Figure 3.8. We only need to store two rows, the current row
we are processing and the previous row we have processed. To compute row 1, we only need
to know the columns in row 0 that are set to true in reachable. To compute row 2, we
similarly only need to know the columns in row 1 that are set to true in reachable. In
general, to compute row g, we only need values from the previous row g − 1. So, instead
of storing a boolean matrix reachable[g][money] of size 20 × 201, we can simply store
reachable[2][money] of size 2 × 201. We can use this programming trick to reference one
row as the ‘previous’ row and another row as the ‘current’ row (e.g. prev = 0, cur = 1)
and then swap them (e.g. now prev = 1, cur = 0) as we compute the bottom-up DP row
by row. Note that for this problem, the memory savings are not significant. For harder DP
problems, for example where there might be thousands of garment models instead of 20, this
space saving trick can be important.

Top-Down versus Bottom-Up DP


Although both styles use ‘tables’, the way the bottom-up DP table is filled is different to that
of the top-down DP memo table. In the top-down DP, the memo table entries are filled ‘as
needed’ through the recursion itself. In the bottom-up DP, we used a correct ‘DP table filling
order’ to compute the values such that the previous values needed to process the current cell
have already been obtained. This table filling order is the topological order of the implicit
DAG (this will be explained in more detail in Section 4.7.1) in the recurrence structure. For
most DP problems, a topological order can be achieved simply with the proper sequencing
of some (nested) loops.
For most DP problems, these two styles are equally good and the decision to use a
particular DP style is a matter of preference. However, for harder DP problems, one of the

101
3.5. DYNAMIC PROGRAMMING 
c Steven & Felix

styles can be better than the other. To help you understand which style that you should
use when presented with a DP problem, please study the trade-offs between top-down and
bottom-up DPs listed in Table 3.2.

Top-Down Bottom-Up
Pros: Pros:
1. It is a natural transformation from the 1. Faster if many sub-problems are revisited
normal Complete Search recursion as there is no overhead from recursive calls
2. Computes the sub-problems only when 2. Can save memory space with the ‘space
necessary (sometimes this is faster) saving trick’ technique
Cons: Cons:
1. Slower if many sub-problems are revis- 1. For programmers who are inclined to re-
ited due to function call overhead (this is not cursion, this style may not be intuitive
usually penalized in programming contests)
2. If there are M states, an O(M) table size 2. If there are M states, bottom-up DP
is required, which can lead to MLE for some visits and fills the value of all these M states
harder problems (except if we use the trick
in Section 8.3.4)

Table 3.2: DP Decision Table

Displaying the Optimal Solution


Many DP problems request only for the value of the optimal solution (like the UVa 11450
above). However, many contestants are caught off-guard when they are also required to print
the optimal solution. We are aware of two ways to do this.
The first way is mainly used in the bottom-up DP approach (which is still applicable for
top-down DPs) where we store the predecessor information at each state. If there are more
than one optimal predecessors and we have to output all optimal solutions, we can store
those predecessors in a list. Once we have the optimal final state, we can do backtracking
from the optimal final state and follow the optimal transition(s) recorded at each state until
we reach one of the base cases. If the problem asks for all optimal solutions, this backtracking
routine will print them all. However, most problem authors usually set additional output
criteria so that the selected optimal solution is unique (for easier judging).
Example: See Figure 3.8, bottom. The optimal final state is reachable[2][1]. The
predecessor of this optimal final state is state reachable[1][2]. We now backtrack to
reachable[1][2]. Next, see Figure 3.8, middle. The predecessor of state reachable[1][2]
is state reachable[0][12]. We then backtrack to reachable[0][12]. As this is already
one of the initial base states (at the first row), we know that an optimal solution is: (20→12)
= price 8, then (12→2) = price 10, then (2→1) = price 1. However, as mentioned earlier
in the problem description, this problem may have several other optimal solutions, e.g. We
can also follow the path: reachable[2][1] → reachable[1][6] → reachable[0][16]
which represents another optimal solution: (20→16) = price 4, then (16→6) = price 10,
then (6→1) = price 5.
The second way is applicable mainly to the top-down DP approach where we utilize the
strength of recursion and memoization to do the same job. Using the top-down DP code
shown in Approach 4 above, we will add another function void print shop(int money,
int g) that has the same structure as int shop(int money, int g) except that it uses
the values stored in the memo table to reconstruct the solution. A sample implementation
(that only prints out one optimal solution) is shown below:

102
CHAPTER 3. PROBLEM SOLVING PARADIGMS 
c Steven & Felix

void print_shop(int money, int g) { // this function returns void


if (money < 0 || g == C) return; // similar base cases
for (int model = 1; model <= price[g][0]; model++) // which model is opt?
if (shop(money - price[g][model], g + 1) == memo[money][g]) {
printf("%d%c", price[g][model], g == C-1 ? ’\n’ : ’-’); // this one
print_shop(money - price[g][model], g + 1); // recurse to this state
break; // do not visit other states
} }

Exercise 3.5.1.1: To verify your understanding of UVa 11450 problem discussed in this
section, determine what is the output for test case D below?
Test case D with M = 25, C = 3:
Price of the 3 models of garment g = 0 → 6 4 8
Price of the 2 models of garment g = 1 → 10 6
Price of the 4 models of garment g = 2 → 7 3 1 5
Exercise 3.5.1.2: Is the following state formulation shop(g, model), where g represents
the current garment and model represents the current model, appropriate and exhaustive
for UVa 11450 problem?
Exercise 3.5.1.3: Add the space saving trick to the bottom-up DP code in Approach 5!

3.5.2 Classical Examples


The problem UVa 11450 - Wedding Shopping above is a (relatively easy) non-classical DP
problem, where we had to come up with the correct DP states and transitions by ourself.
However, there are many other classical problems with efficient DP solutions, i.e. their
DP states and transitions are well-known. Therefore, such classical DP problems and their
solutions should be mastered by every contestant who wishes to do well in ICPC or IOI! In
this section, we list down six classical DP problems and their solutions. Note: Once you
understand the basic form of these DP solutions, try solving the programming exercises that
enumerate their variants.

1. Max 1D Range Sum


Abridged problem statement of UVa 507 - Jill Rides Again: Given an integer array A contain-
ing n ≤ 20K non-zero integers, determine the maximum (1D) range sum of A. In other words,
find the maximum Range Sum Query (RSQ) between two indices i and j in [0..n-1], that
is: A[i] + A[i+1] + A[i+2] +...+ A[j] (also see Section 2.4.3 and 2.4.4).
A Complete Search algorithm that tries all possible O(n2) pairs of i and j, computes
the required RSQ(i, j) in O(n), and finally picks the maximum one runs in an overall time
complexity of O(n3 ). With n up to 20K, this is a TLE solution.
In Section 2.4.4, we have discussed the following DP strategy: Pre-process array A by com-
puting A[i] += A[i-1] ∀i ∈ [1..n-1] so that A[i] contains the sum of integers in subar-
ray A[0..i]. We can now compute RSQ(i, j) in O(1): RSQ(0, j) = A[j] and RSQ(i, j)
= A[j] - A[i-1] ∀i > 0. With this, the Complete Search algorithm above can be made
to run in O(n2 ). For n up to 20K, this is still a TLE approach. However, this technique is
still useful in other cases (see the usage of this 1D Range Sum in Section 8.4.2).

103
3.5. DYNAMIC PROGRAMMING 
c Steven & Felix

There is an even better algorithm for this problem. The main part of Jay Kadane’s O(n)
(can be viewed as a greedy or DP) algorithm to solve this problem is shown below.

// inside int main()


int n = 9, A[] = { 4, -5, 4, -3, 4, 4, -4, 4, -5 }; // a sample array A
int sum = 0, ans = 0; // important, ans must be initialized to 0
for (int i = 0; i < n; i++) { // linear scan, O(n)
sum += A[i]; // we greedily extend this running sum
ans = max(ans, sum); // we keep the maximum RSQ overall
if (sum < 0) sum = 0; // but we reset the running sum
} // if it ever dips below 0
printf("Max 1D Range Sum = %d\n", ans);

Source code: ch3 04 Max1DRangeSum.cpp/java

The key idea of Kadane’s algorithm is to keep a running sum of the integers seen so far and
greedily reset that to 0 if the running sum dips below 0. This is because re-starting from
0 is always better than continuing from a negative running sum. Kadane’s algorithm is the
required algorithm to solve this UVa 507 problem as n ≤ 20K.
Note that we can also view this Kadane’s algorithm as a DP solution. At each step,
we have two choices: We can either leverage the previously accumulated maximum sum, or
begin a new range. The DP variable dp(i) thus represents the maximum sum of a range of
integers that ends with element A[i]. Thus, the final answer is the maximum over all the
values of dp(i) where i ∈ [0..n-1]. If zero-length ranges are allowed, then 0 must also be
considered as a possible answer. The implementation above is essentially an efficient version
that utilizes the space saving trick discussed earlier.

2. Max 2D Range Sum


Abridged problem statement of UVa 108 - Maximum Sum: Given an n × n (1 ≤ n ≤ 100)
square matrix of integers A where each integer ranges from [-127..127], find a sub-matrix
of A with the maximum sum. For example: The 4 × 4 matrix (n = 4) in Table 3.3.A below
has a 3 × 2 sub-matrix on the lower-left with maximum sum of 9 + 2 - 4 + 1 - 1 + 8 = 15.

Table 3.3: UVa 108 - Maximum Sum

Attacking this problem naı̈vely using a Complete Search as shown below does not work as
it runs in O(n6). For the largest test case with n = 100, an O(n6 ) algorithm is too slow.
maxSubRect = -127*100*100; // the lowest possible value for this problem
for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) // start coordinate
for (int k = i; k < n; k++) for (int l = j; l < n; l++) { // end coord
subRect = 0; // sum the items in this sub-rectangle
for (int a = i; a <= k; a++) for (int b = j; b <= l; b++)
subRect += A[a][b];
maxSubRect = max(maxSubRect, subRect); } // the answer is here

104
CHAPTER 3. PROBLEM SOLVING PARADIGMS 
c Steven & Felix

The solution for the Max 1D Range Sum in the previous subsection can be extended to two
(or more) dimensions as long as the inclusion-exclusion principle is properly applied. The
only difference is that while we dealt with overlapping sub-ranges in Max 1D Range Sum,
we will deal with overlapping sub-matrices in Max 2D Range Sum. We can turn the n × n
input matrix into an n × n cumulative sum matrix where A[i][j] no longer contains its
own value, but the sum of all items within sub-matrix (0, 0) to (i, j). This can be done
simultaneously while reading the input and still runs in O(n2 ). The code shown below turns
the input square matrix (see Table 3.3.A) into a cumulative sum matrix (see Table 3.3.B).

scanf("%d", &n); // the dimension of input square matrix


for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) {
scanf("%d", &A[i][j]);
if (i > 0) A[i][j] += A[i - 1][j]; // if possible, add from top
if (j > 0) A[i][j] += A[i][j - 1]; // if possible, add from left
if (i > 0 && j > 0) A[i][j] -= A[i - 1][j - 1]; // avoid double count
} // inclusion-exclusion principle

With the sum matrix, we can answer the sum of any sub-matrix (i, j) to (k, l) in O(1)
using the code below. For example, let’s compute the sum of (1, 2) to (3, 3). We split
the sum into 4 parts and compute A[3][3] - A[0][3] - A[3][1] + A[0][1] = -3 - 13
- (-9) + (-2) = -9 as highlighted in Table 3.3.C. With this O(1) DP formulation, the
Max 2D Range Sum problem can now be solved in O(n4 ). For the largest test case of UVa
108 with n = 100, this is still fast enough.

maxSubRect = -127*100*100; // the lowest possible value for this problem


for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) // start coordinate
for (int k = i; k < n; k++) for (int l = j; l < n; l++) { // end coord
subRect = A[k][l]; // sum of all items from (0, 0) to (k, l): O(1)
if (i > 0) subRect -= A[i - 1][l]; // O(1)
if (j > 0) subRect -= A[k][j - 1]; // O(1)
if (i > 0 && j > 0) subRect += A[i - 1][j - 1]; // O(1)
maxSubRect = max(maxSubRect, subRect); } // the answer is here

Source code: ch3 05 UVa108.cpp/java

From these two examples—the Max 1D and 2D Range Sum Problems—we can see that not
every range problem requires a Segment Tree or a Fenwick Tree as discussed in Section 2.4.3
or 2.4.4. Static-input range-related problems are often solvable with DP techniques. It is
also worth mentioning that the solution for a range problem is very natural to produce with
bottom-up DP techniques as the operand is already a 1D or a 2D array. We can still write
the recursive top-down solution for a range problem, but the solution is not as natural.

3. Longest Increasing Subsequence (LIS)


Given a sequence {A[0], A[1],..., A[n-1]}, determine its Longest Increasing Subse-
quence (LIS)13 . Note that these ‘subsequences’ are not necessarily contiguous. Example:
n = 8, A = {−7, 10, 9, 2, 3, 8, 8, 1}. The length-4 LIS is {-7, 2, 3, 8}.
13
There are other variants of this problem, including the Longest Decreasing Subsequence and Longest
Non Increasing/Decreasing Subsequence. The increasing subsequences can be modeled as a Directed Acyclic
Graph (DAG) and finding the LIS is equivalent to finding the Longest Paths in the DAG (see Section 4.7.1).

105
3.5. DYNAMIC PROGRAMMING 
c Steven & Felix

Figure 3.9: Longest Increasing Subsequence

As mentioned in Section 3.1, a naı̈ve Complete Search that enumerates all possible subse-
quences to find the longest increasing one is too slow as there are O(2n ) possible subsequences.
Instead of trying all possible subsequences, we can consider the problem with a different ap-
proach. We can write the state of this problem with just one parameter: i. Let LIS(i) be
the LIS ending at index i. We know that LIS(0) = 1 as the first number in A is itself a
subsequence. For i ≥ 1, LIS(i) is slightly more complex. We need to find the index j such
that j < i and A[j] < A[i] and LIS(j) is the largest. Once we have found this index j,
we know that LIS(i) = 1 + LIS(j). We can write this recurrence formally as:

1. LIS(0) = 1 // the base case


2. LIS(i) = max(LIS(j) + 1), ∀j ∈ [0..i-1] and A[j] < A[i] // the recursive case,
one more than the previous best solution ending at j for all j < i.
The answer is the largest value of LIS(k) ∀k ∈ [0..n-1].
Now let’s see how this algorithm works (also see Figure 3.9):

• LIS(0) is 1, the first number in A = {-7}, the base case.


• LIS(1) is 2, as we can extend LIS(0) = {-7} with {10} to form {-7, 10} of length 2.
The best j for i = 1 is j = 0.
• LIS(2) is 2, as we can extend LIS(0) = {-7} with {9} to form {-7, 9} of length 2.
We cannot extend LIS(1) = {-7, 10} with {9} as it is non increasing.
The best j for i = 2 is j = 0.
• LIS(3) is 2, as we can extend LIS(0) = {-7} with {2} to form {-7, 2} of length 2.
We cannot extend LIS(1) = {-7, 10} with {2} as it is non-increasing.
We also cannot extend LIS(2) = {-7, 9} with {2} as it is also non-increasing.
The best j for i = 3 is j = 0.
• LIS(4) is 3, as we can extend LIS(3) = {-7, 2} with {3} to form {-7, 2, 3}.
This is the best choice among the possibilities.
The best j for i = 4 is j = 3.
• LIS(5) is 4, as we can extend LIS(4) = {-7, 2, 3} with {8} to form {-7, 2, 3, 8}.
This is the best choice among the possibilities.
The best j for i = 5 is j = 4.
• LIS(6) is 4, as we can extend LIS(4) = {-7, 2, 3} with {8} to form {-7, 2, 3, 8}.
This is the best choice among the possibilities.
The best j for i = 6 is j = 4.
• LIS(7) is 2, as we can extend LIS(0) = {-7} with {1} to form {-7, 1}.
This is the best choice among the possibilities.
The best j for i = 7 is j = 0.
• The answers lie at LIS(5) or LIS(6); both values (LIS lengths) are 4.
See that the index k where LIS(k) is the highest can be anywhere in [0..n-1].

106
CHAPTER 3. PROBLEM SOLVING PARADIGMS 
c Steven & Felix

There are clearly many overlapping sub-problems in LIS problem because to compute LIS(i),
we need to compute LIS(j) ∀j ∈ [0..i-1]. However, there are only n distinct states, the
indices of the LIS ending at index i, ∀i ∈ [0..n-1]. As we need to compute each state
with an O(n) loop, this DP algorithm runs in O(n2 ).
If needed, the LIS solution(s) can be reconstructed by storing the predecessor information
(the arrows in Figure 3.9) and tracing the arrows from index k that contain the highest value
of LIS(k). For example, LIS(5) is the optimal final state. Check Figure 3.9. We can trace
the arrows as follow: LIS(5) → LIS(4) → LIS(3) → LIS(0), so the optimal solution (read
backwards) is index {0, 3, 4, 5} or {-7, 2, 3, 8}.

The LIS problem can also be solved using the output-sensitive O(n log k) greedy +
D&C algorithm (where k is the length of the LIS) instead of O(n2 ) by maintaining an
array that is always sorted and therefore amenable to binary search. Let array L be an
array such that L(i) represents the smallest ending value of all length-i LISs found
so far. Though this definition is slightly complicated, it is easy to see that it is always
ordered—L(i-1) will always be smaller than L(i) as the second-last element of any
LIS (of length-i) is smaller than its last element. As such, we can binary search array L
to determine the longest possible subsequence we can create by appending the current
element A[i]—simply find the index of the last element in L that is less than A[i].
Using the same example, we will update array L step by step using this algorithm:
• Initially, at A[0] = -7, we have L = {-7}.
• We can insert A[1] = 10 at L[1] so that we have a length-2 LIS, L = {-7, 10}.
• For A[2] = 9, we replace L[1] so that we have a ‘better’ length-2 LIS ending:
L = {-7, 9}.
This is a greedy strategy. By storing the LIS with smaller ending value,
we maximize our ability to further extend the LIS with future values.
• For A[3] = 2, we replace L[1] to get an ‘even better’ length-2 LIS ending:
L = {-7, 2}.
• We insert A[4] = 3 at L[2] so that we have a longer LIS, L = {-7, 2, 3}.
• We insert A[5] = 8 at L[3] so that we have a longer LIS, L = {-7, 2, 3, 8}.
• For A[6] = 8, nothing changes as L[3] = 8.
L = {-7, 2, 3, 8} remains unchanged.
• For A[7] = 1, we improve L[1] so that L = {-7, 1, 3, 8}.
This illustrates how the array L is not the LIS of A. This step is important as
there can be longer subsequences in the future that may extend the length-2
subsequence at L[1] = 1. For example, try this test case: A = {-7, 10, 9, 2,
3, 8, 8, 1, 2, 3, 4}. The length of LIS for this test case is 5.
• The answer is the largest length of the sorted array L at the end of the process.

Source code: ch3 06 LIS.cpp/java

4. 0-1 Knapsack (Subset Sum)


Problem14 : Given n items, each with its own value Vi and weight Wi , ∀i ∈ [0..n-1], and a
maximum knapsack size S, compute the maximum value of the items that we can carry, if
we can either15 ignore or take a particular item (hence the term 0-1 for ignore/take).
14
This problem is also known as the Subset Sum problem. It has a similar problem description: Given a
set of integers and an integer S, is there a (non-empty) subset that has a sum equal to S?
15
There are other variants of this problem, e.g. the Fractional Knapsack problem with Greedy solution.

107
3.5. DYNAMIC PROGRAMMING 
c Steven & Felix

Example: n = 4, V = {100, 70, 50, 10}, W = {10, 4, 6, 12}, S = 12.


If we select item 0 with weight 10 and value 100, we cannot take any other item. Not optimal.
If we select item 3 with weight 12 and value 10, we cannot take any other item. Not optimal.
If we select item 1 and 2, we have total weight 10 and total value 120. This is the maximum.

Solution: Use these Complete Search recurrences val(id, remW) where id is the index of
the current item to be considered and remW is the remaining weight left in the knapsack:
1. val(id, 0) = 0 // if remW = 0, we cannot take anything else
2. val(n, remW) = 0 // if id = n, we have considered all items
3. if W[id] > remW, we have no choice but to ignore this item
val(id, remW) = val(id + 1, remW)
4. if W[id] ≤ remW, we have two choices: ignore or take this item; we take the maximum
val(id, remW) = max(val(id + 1, remW), V[id] + val(id + 1, remW - W[id]))

The answer can be found by calling value(0, S). Note the overlapping sub-problems in this
0-1 Knapsack problem. Example: After taking item 0 and ignoring item 1-2, we arrive at
state (3, 2)—at the third item (id = 3) with two units of weight left (remW = 2). After
ignoring item 0 and taking item 1-2, we also arrive at the same state (3, 2). Although
there are overlapping sub-problems, there are only O(nS) possible distinct states (as id can
vary between [0..n-1] and remW can vary between [0..S])! We can compute each of these
states in O(1), thus the overall time complexity16 of this DP solution is O(nS).
Note: The top-down version of this DP solution is often faster than the bottom-up
version. This is because not all states are actually visited, and hence the critical DP states
involved are actually only a (very small) subset of the entire state space. Remember: The
top-down DP only visits the required states whereas bottom-up DP visits all distinct states.
Both versions are provided in our source code library.
Source code: ch3 07 UVa10130.cpp/java

5. Coin Change (CC) - The General Version


Problem: Given a target amount V cents and a list of denominations for n coins, i.e. we
have coinValue[i] (in cents) for coin types i ∈ [0..n-1], what is the minimum number
of coins that we must use to represent V ? Assume that we have unlimited supply of coins
of any type (also see Section 3.4.1).
Example 1: V = 10, n = 2, coinValue = {1, 5}; We can use:
A. Ten 1 cent coins = 10 × 1 = 10; Total coins used = 10
B. One 5 cents coin + Five 1 cent coins = 1 × 5 + 5 × 1 = 10; Total coins used = 6
C. Two 5 cents coins = 2 × 5 = 10; Total coins used = 2 → Optimal
We can use the Greedy algorithm if the coin denominations are suitable (see Section 3.4.1).
Example 1 above is solvable with the Greedy algorithm. However, for general cases, we have
to use DP. See Example 2 below:
Example 2: V = 7, n = 4, coinValue = {1, 3, 4, 5}
The Greedy approach will produce 3 coins as its result as 5+1+1 = 7, but the optimal
solution is actually 2 coins (from 4+3)!

Solution: Use these Complete Search recurrence relations for change(value), where value
is the remaining amount of cents that we need to represent in coins:
16
If S is large such that N S >> 1M , this DP solution is not feasible, even with the space saving trick!

108
CHAPTER 3. PROBLEM SOLVING PARADIGMS 
c Steven & Felix

1. change(0) = 0 // we need 0 coins to produce 0 cents


2. change(< 0) = ∞ // in practice, we can return a large positive value
3. change(value) = 1 + min(change(value - coinValue[i])) ∀i ∈ [0..n-1]

The answer can be found in the return value of change(V).

Figure 3.10: Coin Change

Figure 4.2.3 shows that:


change(0) = 0 and change(< 0) = ∞: These are the base cases.
change(1) = 1, from 1 + change(1-1), as 1 + change(1-5) is infeasible (returns ∞).
change(2) = 2, from 1 + change(2-1), as 1 + change(2-5) is also infeasible (returns ∞).
... same thing for change(3) and change(4).
change(5) = 1, from 1 + change(5-5) = 1 coin, smaller than 1 + change(5-1) = 5 coins.
... and so on until change(10).
The answer is in change(V), which is change(10) = 2 in this example.

We can see that there are a lot of overlapping sub-problems in this Coin Change problem
(e.g. both change(10) and change(6) require the value of change(5)). However, there are
only O(V ) possible distinct states (as value can vary between [0..V])! As we need to try
n types of coins per state, the overall time complexity of this DP solution is O(nV ).

A variant of this problem is to count the number of possible (canonical) ways to get
value V cents using a list of denominations of n coins. For example 1 above, the answer
is 3: {1+1+1+1+1 + 1+1+1+1+1, 5 + 1+1+1+1+1, 5 + 5}.
Solution: Use these Complete Search recurrence relation: ways(type, value), where
value is the same as above but we now have one more parameter type for the index
of the coin type that we are currently considering. This second parameter type is
important as this solution considers the coin types sequentially. Once we choose to
ignore a certain coin type, we should not consider it again to avoid double-counting:
1. ways(type, 0) = 1 // one way, use nothing
2. ways(type, <0) = 0 // no way, we cannot reach negative value
3. ways(n, value) = 0 // no way, we have considered all coin types ∈ [0..n-1]
4. ways(type, value) = ways(type + 1, value) + // if we ignore this coin type,
ways(type, value - coinValue[type]) // plus if we use this coin type

There are only O(nV ) possible distinct states. Since each state can be computed in
O(1), the overall time complexity17 of this DP solution is O(nV ). The answer can be
found by calling ways(0, V). Note: If the coin values are not changed and you are
given many queries with different V, then we can choose not to reset the memo table.
Therefore, we run this O(nV ) algorithm once and just perform an O(1) lookup for
subsequent queries.

Source code (this coin change variant): ch3 08 UVa674.cpp/java

17
If V is large such that nV >> 1M , this DP solution is not feasible even with the space saving trick!

109
3.5. DYNAMIC PROGRAMMING 
c Steven & Felix

6. Traveling Salesman Problem (TSP)


Problem: Given n cities and their pairwise distances in the form of a matrix dist of size
n × n, compute the cost of making a tour18 that starts from any city s, goes through all the
other n − 1 cities exactly once, and finally returns to the starting city s.
Example: The graph shown in Figure 3.11 has n = 4 cities. Therefore, we have 4! = 24
possible tours (permutations of 4 cities). One of the minimum tours is A-B-C-D-A with a
cost of 20+30+12+35 = 97 (notice that there can be more than one optimal solution).

Figure 3.11: A Complete Graph

A ‘brute force’ TSP solution (either iterative or recursive) that tries all O((n − 1)!) possible
tours (fixing the first city to vertex A in order to take advantage of symmetry) is only
effective when n is at most 12 as 11! ≈ 40M. When n > 12, such brute force solutions will
get a TLE in programming contests. However, if there are multiple test cases, the limit for
such ‘brute force’ TSP solution is probably just n = 11.
We can utilize DP for TSP since the computation of sub-tours is clearly overlapping, e.g.
the tour A − B − C−(n − 3) other cities that finally return to A clearly overlaps the tour
A − C − B−the same (n − 3) other cities that also return to A. If we can avoid re-computing
the lengths of such sub-tours, we can save a lot of computation time. However, a distinct
state in TSP depends on two parameters: The last city/vertex visited pos and something
that we may have not seen before—a subset of visited cities.
There are many ways to represent a set. However, since we are going to pass this set
information around as a parameter of a recursive function (if using top-down DP), the
representation we use must be lightweight and efficient! In Section 2.2, we have presented
a viable option for this usage: The bitmask. If we have n cities, we use a binary integer of
length n. If bit i is ‘1’ (on), we say that item (city) i is inside the set (it has been visited)
and item i is not inside the set (and has not been visited) if the bit is instead ‘0’ (off). For
example: mask= 1810 = 100102 implies that items (cities) {1, 4} are in19 the set (and have
been visited). Recall that to check if bit i is on or off, we can use mask & (1 << i). To set
bit i, we can use mask |= (1 << i).

Solution: Use these Complete Search recurrence relations for tsp(pos, mask):
1. tsp(pos, 2n − 1) = dist[pos][0] // all cities have been visited, return to starting city
// Note: mask = (1 << n) - 1 or 2n − 1 implies that all n bits in mask are on.
2. tsp(pos, mask) = min(dist[pos][nxt] + tsp(nxt, mask | (1 << nxt)))
// ∀ nxt ∈ [0..n-1], nxt != pos, and (mask & (1 << nxt)) is ‘0’ (turned off)
// We basically tries all possible next cities that have not been visited before at each step.

There are only O(n × 2n ) distinct states because there are n cities and we remember up to
2n other cities that have been visited in each tour. Each state can be computed in O(n),
18
Such a tour is called a Hamiltonian tour, which is a cycle in an undirected graph which visits each vertex
exactly once and also returns to the starting vertex.
19
Remember that in mask, indices starts from 0 and are counted from the right.

110
CHAPTER 3. PROBLEM SOLVING PARADIGMS 
c Steven & Felix

thus the overall time complexity of this DP solution is O(2n × n2 ). This allows us to solve
up to20 n ≈ 16 as 162 × 216 ≈ 17M. This is not a huge improvement over the brute force
solution but if the programming contest problem involving TSP has input size 11 ≤ n ≤ 16,
then DP is the solution, not brute force. The answer can be found by calling tsp(0, 1):
We start from city 0 (we can start from any vertex; but the simplest choice is vertex 0) and
set mask = 1 so that city 0 is never re-visited again.
Usually, DP TSP problems in programming contests require some kind of graph prepro-
cessing to generate the distance matrix dist before running the DP solution. These variants
are discussed in Section 8.4.3.
DP solutions that involve a (small) set of Booleans as one of the parameters are more
well known as the DP with bitmask technique. More challenging DP problems involving this
technique are discussed in Section 8.3 and 9.2.
Visualization: www.comp.nus.edu.sg/∼stevenha/visualization/rectree.html
Source code: ch3 09 UVa10496.cpp/java

Exercise 3.5.2.1: The solution for the Max 2D Range Sum problem runs in O(n4 ). Actually,
there exists an O(n3 ) solution that combines the DP solution for the Max Range 1D Sum
problem on one dimension and uses the same idea as proposed by Kadane on the other
dimension. Solve UVa 108 with an O(n3 ) solution!
Exercise 3.5.2.2: The solution for the Range Minimum Query(i, j) on 1D arrays in Sec-
tion 2.4.3 uses Segment Tree. This is overkill if the given array is static and unchanged
throughout all the queries. Use a DP technique to answer RMQ(i, j) in O(n log n) pre-
processing and O(1) per query.
Exercise 3.5.2.3: Solve the LIS problem using the O(n log k) solution and also reconstruct
one of the LIS.
Exercise 3.5.2.4: Can we use an iterative Complete Search technique that tries all possible
subsets of n items as discussed in Section 3.2.1 to solve the 0-1 Knapsack problem? What
are the limitations, if any?
Exercise 3.5.2.5*: Suppose we add one more parameter to this classic 0-1 Knapsack prob-
lem. Let Ki denote the number of copies of item i for use in the problem. Example: n = 2,
V = {100, 70}, W = {5, 4}, K = {2, 3}, S = 17 means that there are two copies of item 0
with weight 5 and value 100 and there are three copies of item 1 with weight 4 and value
70. The optimal solution for this example is to take one of item 0 and three of item 1, with
a total weight of 17 and total value
n−1310. Solve new variant of the problem assuming that
1 ≤ n ≤ 500, 1 ≤ S ≤ 2000, n ≤ i=0 Ki ≤ 40000! Hint: Every integer can be written as a
sum of powers of 2.
Exercise 3.5.2.6*: The DP TSP solution shown in this section can still be slightly enhanced
to make it able to solve test case with n = 17 in contest environment. Show the required
minor change to make this possible! Hint: Consider symmetry!
Exercise 3.5.2.7*: On top of the minor change asked in Exercise 3.5.2.5*, what other
change(s) is/are needed to have a DP TSP solution that is able to handle n = 18 (or even
n = 19, but with much lesser number of test cases)?

20
As programming contest problems usually require exact solutions, the DP-TSP solution presented here
is already one of the best solutions. In real life, the TSP often needs to be solved for instances with thousands
of cities. To solve larger problems like that, we have non-exact approaches like the ones presented in [26].

111
3.5. DYNAMIC PROGRAMMING 
c Steven & Felix

3.5.3 Non-Classical Examples


Although DP is the single most popular problem type with the highest frequency of appear-
ance in recent programming contests, the classical DP problems in their pure forms usually
never appear in modern ICPCs or IOIs again. We study them to understand DP, but we
have to learn to solve many other non-classical DP problems (which may become classic in
the near future) and develop our ‘DP skills’ in the process. In this subsection, we discuss
two more non-classical examples, adding to the UVa 11450 - Wedding Shopping problem
that we have discussed in detail earlier. We have also selected some easier non-classical DP
problems as programming exercises. Once you have cleared most of these problems, you are
welcome to explore the more challenging ones in the other sections in this book, e.g. Section
4.7.1, 5.4, 5.6, 6.5, 8.3, 9.2, 9.21, etc.

1. UVa 10943 - How do you add?


Abridged problem description: Given an integer n, how many ways can K non-negative
integers less than or equal to n add up to n? Constraints: 1 ≤ n, K ≤ 100. Example: For
n = 20 and K = 2, there are 21 ways: 0 + 20, 1 + 19, 2 + 18, 3 + 17, . . . , 20 + 0.
Mathematically, the number of ways can be expressed as (n+k−1) C(k−1) (see Section 5.4.2
about Binomial Coefficients). We will use this simple problem to re-illustrate Dynamic
Programming principles that we have discussed in this section, especially the process of
deriving appropriate states for a problem and deriving correct transitions from one state to
another given the base case(s).
First, we have to determine the parameters of this problem to be selected to represent
distinct states of this problem. There are only two parameters in this problem, n and K.
Therefore, there are only 4 possible combinations:
1. If we do not choose any of them, we cannot represent a state. This option is ignored.
2. If we choose only n, then we do not know how many numbers ≤ n have been used.
3. If we choose only K, then we do not know the target sum n.
4. Therefore, the state of this problem should be represented by a pair (or tuple) (n, K).
The order of chosen parameter(s) does not matter, i.e. the pair (K, n) is also OK.
Next, we have to determine the base case(s). It turns out that this problem is very easy
when K = 1. Whatever n is, there is only one way to add exactly one number less than or
equal to n to get n: Use n itself