Reflections of an ICPC World Finalist, 20 Years Later
A personal story of pursuing insights and beauty, and sticking to your own values.
My software engineering career so far has been quite boring. Like many people in tech, I moved to the US to join a big tech company after the financial crisis was over, and then started to climb the job ladder, one level at a time. Sure, there are interesting moments when you are building things that affect billions of people or billions of dollars, but for confidentiality reasons, I can’t talk about them anyway.
If I were to find something to brag about publicly, I would have to go back to 20 years ago. That was on Apr 12, 2006, when I, with two teammates, competed at the International Collegiate Programming Contest (ICPC) World Finals against 82 other teams from around the world. For those who don’t know about ICPC, this is what Google says about it: “the ICPC is globally recognized as the oldest, largest and most prestigious algorithmic programming competition at college level”.
What makes this achievement more brag-worthy is that, when we qualified for the world final the year before in an Asian regional contest, it was less than two years since I started to learn how to use a computer and how to write code.
However, the reason I want to talk about this today is not to brag about how “smart” I am or I was - in fact, getting into the circle of competitive programming allowed me to witness what truly smart people look like, which confirmed my belief that I am not one of those. Instead, I want to take this 20 year mark as a moment for reflection - what competitive programming meant for me, what made me competitive and what I learned about myself through that journey.

Appreciation
For one, I am forever grateful for what competitive programming has brought into my life, and for the older schoolmates that chose me to form their team. As a kid from a poor and insular family, competing in the ICPC regional contests allowed me to travel around the country, staying in nice hotels and visiting places that I only knew from books. Travel expenses were paid by my college, which I couldn’t afford otherwise. Qualifying for the world final wasn’t just reaching the highest level of competition; it was my free ticket to see the other side of the world. After the world final, we were invited by Google to its Beijing office to meet with Kaifu Lee in person, while staying in 5-star hotels and having luxury dinners. For a short period of time, it almost felt like I was a celebrity.
I feel lucky that I got into competitive programming at the right time, a time when the scale and influence of ICPC in China just started its rapid growth. There were a growing number of regional contests and a growing number of tickets to the world final, but for most universities and students, participating in competitive programming remained largely a hobby. There was little structured training, and not many people had competitive programming experience before college. If I had entered college 5 or 10 years later with zero programming experience, would I have achieved the same outcome as I did? The answer might be yes, but I would definitely need a lot more effort, and I would need to be a lot more contest oriented. For example, I would have to practice intensively to be a fast programmer, and I would have to hone my knowledge in various areas of competitive programming to make sure there is no blind spot. To me, that would definitely become incredibly boring.
The Source of Joy
Throughout my tenure as a competitive programmer, being competitive in contests was never my focus; it was the byproduct of the joy of problem solving. I didn’t stress myself to be a fast programmer. I cared much more about the beauty of the code than the speed of coding. I would spend a lot of time thinking how to reduce multiple cases into a single case, If I didn’t see how the complexity could be justified.
Among people reaching a similar competitive level, I was among those who have written the least amount of code. There were multiple reasons for that - I was picky at which problems to solve, I preferred to think for days on a challenging problem before looking for help, and I’d prove the correctness of my algorithm before writing the code. All that I did might be considered inefficient for competitive programming, where the popular strategy is to absorb knowledge and practice as much as you can, but to me, the source of joy, and the goal worth pursuing is not the speed you can solve problems; it is what novel problems you can solve given enough time, and the insights you gain and the beauty you discover after all the struggles.
I can still remember one of those joyful moments till this day. One day, I encountered a problem which was to find the length of the shortest round trip from point S to point T on an undirected graph, without visiting the same edge twice.
The natural first reaction is to find the shortest path, remove the visited edges and find another shortest path from the remaining edges. This naive greedy algorithm is incorrect though. For the example above, you will find a round trip of length 11 if you use this greedy algorithm (S->A->B->T->C->S), but the optimal solution has a length of 10 (S->A->T->B->C->S).
If you have learned about network flow algorithms, this can be considered as a special case of a min-cost flow problem, solvable by standard algorithms. But I didn’t know about network algorithms at that time. All I knew was how to calculate the shortest path on graphs.
After a few days’ brain-racking, I solved the problem. It turned out that I just needed a “simple” tweak of the greedy algorithm:
First, find the shortest path in the original undirected graph.
(This is the crucial step) change the edges on the shortest path from undirected edges to directed arcs with only one direction, which is the opposite of the direction on the shortest path. Meanwhile, negate the length of the edge.
Find the shortest path on this new graph.
The sum of the length of the two paths we found is the length of the shortest non-overlapping round trip. As you can see from the picture below , any overlapping edges between the two paths are canceled out, leaving two non-overlapping paths that make a round trip.

When I discovered the algorithm and proved that it is correct, I was in awe. It was unbelievable that such a clean algorithm exists. It was so unobvious at the beginning but after lots of drawings and lots of visual thinking to gain that insight, it became so obvious. Many years later, I realized that I effectively rediscovered Bhandari’s algorithm, which was first described by Bhandari in 1999, who derived the algorithm based on Suurballe’s algorithm discovered in 1974.
Such moments of joy, big or small, was what kept me motivated in competitive programming.
Growing Out
As I look back, I realized my little achievement in competitive programming was not totally a coincidence. ICPC is a team work; by focusing on what appealed to me, and by not focusing at being a good coder and being all-around, I created a differentiator, which made myself useful for a team from early on.
As time passed by, I did grow to become a good individual coder as well - at my best time, I won the 13th place in a national invitational contest - but the joy from solving those problems also became less and less. My interests shifted towards real world projects and machine learning. Participation in competitive programming became more of a responsibility, in which I passed my knowledge and experience to my younger teammates.
A programming contest usually consists of 5 to 10 problems and spans a couple of hours. The length of the competition, the closed-ended nature of the contests, limits the types of suitable problems. As you participate more, the novelty of the problems you get become less and less. Eventually, exploitation - familiarity and the speed of coding become the dominant factor, which kills the fun.
Side note: For that matter, I never consider the fact that LLMs are now able to beat the best students in ICPC world finals, or that they are so fluent at producing coding, as a sign of real intelligence, but rather a sign how well exploited those areas have become.

20 Years Later…
20 years have passed and lots of things have changed, but there are a few things that I have been sticking to - my focus on building insights and discovering underlying simplicity and beauty, and my persistence on doing the right thing (or, when I can’t, staying away from what I think is the wrong thing), even they don’t seem to align with the popular definition of “success”. To my delight, my values, and the skills that I built over the years based on my values, continued to be a differentiator, which allowed me to bring unique value in teamwork.
I also found joy in blog writing. Just like solving programming problems, I would contemplate for days and weeks to build insights in my next topic, and I tried my best to convey those insights in the cleanest way (while improving my English along the way). What is better than competitive programming is, I am no longer constrained by the close-ended nature of programming problems. The whole world is open for me to explore.
Am I going to be a successful writer? I don’t know and I don’t really care. But I do know, as long as I focus on gaining my insights and discovering beauty as I explore what appeals to me, I will create a differentiator in my writing from which some people will find value.


![graph G {
layout=neato;
node [shape=circle, width=0.5, fixedsize=true, fontname="Arial", fontsize=12];
edge [fontname="Arial", fontsize=16];
S [pos="0,1!", penwidth=3];
T [pos="4,1!", penwidth=3];
A [pos="1.5,2.5!"];
B [pos="2,1!"];
C [pos="1.5,-0.5!"];
// Weighted Edges
S -- A [label="1", penwidth=3];
S -- B [label="5"];
S -- C [label="3", penwidth=3];
A -- B [label="2"];
B -- T [label="1", penwidth=3];
B -- C [label="1", penwidth=3];
A -- T [label="4", penwidth=3];
C -- T [label="4"];
}
graph G {
layout=neato;
node [shape=circle, width=0.5, fixedsize=true, fontname="Arial", fontsize=12];
edge [fontname="Arial", fontsize=16];
S [pos="0,1!", penwidth=3];
T [pos="4,1!", penwidth=3];
A [pos="1.5,2.5!"];
B [pos="2,1!"];
C [pos="1.5,-0.5!"];
// Weighted Edges
S -- A [label="1", penwidth=3];
S -- B [label="5"];
S -- C [label="3", penwidth=3];
A -- B [label="2"];
B -- T [label="1", penwidth=3];
B -- C [label="1", penwidth=3];
A -- T [label="4", penwidth=3];
C -- T [label="4"];
}](https://substackcdn.com/image/fetch/$s_!W87C!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff64155ce-a380-49d9-bd0a-88161abc4b45_886x694.png)
![digraph G {
layout=neato;
node [shape=circle, width=0.5, fixedsize=true, fontname="Arial", fontsize=12];
edge [fontname="Arial", fontsize=16];
S [pos="0,1!", penwidth=3];
T [pos="4,1!", penwidth=3];
A [pos="1.5,2.5!"];
B [pos="2,1!"];
C [pos="1.5,-0.5!"];
// Weighted Edges
S -> A [label="1", penwidth=3, color="red"];
S -> B [label="5", dir=none];
S -> C [label="3", dir=none];
A -> B [label="2", penwidth=3, color="red"];
# B -> A [label="-2", penwidth=3, color="green", style=dashed];
B -> T [label="1", penwidth=3, color="red"];
C -> B [label="1", dir=none];
A -> T [label="4", dir=none];
C -> T [label="4", dir=none];
}
digraph G {
layout=neato;
node [shape=circle, width=0.5, fixedsize=true, fontname="Arial", fontsize=12];
edge [fontname="Arial", fontsize=16];
S [pos="0,1!", penwidth=3];
T [pos="4,1!", penwidth=3];
A [pos="1.5,2.5!"];
B [pos="2,1!"];
C [pos="1.5,-0.5!"];
// Weighted Edges
S -> A [label="1", penwidth=3, color="red"];
S -> B [label="5", dir=none];
S -> C [label="3", dir=none];
A -> B [label="2", penwidth=3, color="red"];
# B -> A [label="-2", penwidth=3, color="green", style=dashed];
B -> T [label="1", penwidth=3, color="red"];
C -> B [label="1", dir=none];
A -> T [label="4", dir=none];
C -> T [label="4", dir=none];
}](https://substackcdn.com/image/fetch/$s_!ukuT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffdaf6cd4-0993-48f2-a460-dfddbcde82e4_886x694.png)
![digraph G {
layout=neato;
node [shape=circle, width=0.5, fixedsize=true, fontname="Arial", fontsize=12];
edge [fontname="Arial", fontsize=16];
S [pos="0,1!", penwidth=3];
T [pos="4,1!", penwidth=3];
A [pos="1.5,2.5!"];
B [pos="2,1!"];
C [pos="1.5,-0.5!"];
// Weighted Edges
A -> S [label="-1", fontcolor="red"];
S -> B [label="5", dir=none];
S -> C [label="3", dir=none];
B -> A [label="-2", fontcolor="red"];
# B -> A [label="-2", penwidth=3, color="green", style=dashed];
T -> B [label="-1", fontcolor="red"];
C -> B [label="1", dir=none];
A -> T [label="4", dir=none];
C -> T [label="4", dir=none];
}
digraph G {
layout=neato;
node [shape=circle, width=0.5, fixedsize=true, fontname="Arial", fontsize=12];
edge [fontname="Arial", fontsize=16];
S [pos="0,1!", penwidth=3];
T [pos="4,1!", penwidth=3];
A [pos="1.5,2.5!"];
B [pos="2,1!"];
C [pos="1.5,-0.5!"];
// Weighted Edges
A -> S [label="-1", fontcolor="red"];
S -> B [label="5", dir=none];
S -> C [label="3", dir=none];
B -> A [label="-2", fontcolor="red"];
# B -> A [label="-2", penwidth=3, color="green", style=dashed];
T -> B [label="-1", fontcolor="red"];
C -> B [label="1", dir=none];
A -> T [label="4", dir=none];
C -> T [label="4", dir=none];
}](https://substackcdn.com/image/fetch/$s_!ni1u!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffd786d29-5020-4c86-85cd-0298e728a058_886x694.png)
