05. Loops
Computers excel at work that is repeated thousands of times without getting bored. Loops are the tool for telling them to do that — and almost every OSN problem needs them.
for
The for form is used when the number of repetitions is
already known:
for (int i = 1; i <= 5; i++) {
cout << i << " ";
}
cout << "\n";1 2 3 4 5
Three parts inside the parentheses, separated by semicolons:
| Part | When it runs | Example |
|---|---|---|
| initialization | once, before the loop | int i = 1 |
| continue condition | before every iteration | i <= 5 |
| update | after every iteration | i++ |
The order of execution is not simply left to right; it goes like this: the initialization runs once, then the condition is checked, then the body runs, then the update, then the condition is checked again, and so on. Trace the example above:
| Check no. | i when checked |
i <= 5? |
printed | after i++ |
|---|---|---|---|---|
| 1 | 1 | yes | 1 |
2 |
| 2 | 2 | yes | 2 |
3 |
| 3 | 3 | yes | 3 |
4 |
| 4 | 4 | yes | 4 |
5 |
| 5 | 5 | yes | 5 |
6 |
| 6 | 6 | no | — | loop stops |
Note two things that often surprise beginners: the condition is
checked before the body runs (so the body may never run
at all), and after the loop finishes, the value of i has
already gone past its bound.
The variable i created inside
for (int i = ...) only lives for the duration of that loop.
Afterwards it no longer exists, and using it produces
'i' was not declared in this scope.
Commonly used variations:
for (int i = 0; i < n; i++) // 0 to n-1, most common for arrays
for (int i = n; i >= 1; i--) // backwards
for (int i = 0; i < n; i += 2) // step by twoThe number of iterations can be computed without running the program, and this is a habit to build now because Chapter 10 uses it constantly:
| Form | Iterations |
|---|---|
for (int i = 0; i < n; i++) |
n |
for (int i = 1; i <= n; i++) |
n |
for (int i = 0; i <= n; i++) |
n + 1 |
for (int i = 2; i <= n; i++) |
n - 1 |
for (int i = 0; i < n; i += 2) |
about n / 2 |
The third row is the most common off-by-one error:
changing < to <= adds one iteration, and
on an array that extra iteration touches a slot that does not exist.
In contests, for (int i = 0; i < n; i++) is used far
more often than i = 1; i <= n. The reason: array indices
in C++ start at 0. Get used to that form so you are not often off by
one.
while
while is used when the number of repetitions is
not known in advance, only the stopping condition is
known:
int n = 1234;
int banyak_digit = 0;
while (n > 0) {
n /= 10;
banyak_digit++;
}
cout << banyak_digit << "\n"; // 4If the condition never becomes false, the program loops forever, and
on an online judge the result is Time Limit Exceeded. Make sure
there is a line inside the loop that changes the value being checked —
in the example above, n /= 10.
Tracing the example above for n = 1234:
| Check | n |
n > 0? |
n /= 10 |
banyak_digit |
|---|---|---|---|---|
| 1 | 1234 | yes | 123 | 1 |
| 2 | 123 | yes | 12 | 2 |
| 3 | 12 | yes | 1 | 3 |
| 4 | 1 | yes | 0 | 4 |
| 5 | 0 | no | — | 4 |
Choosing for or
while
| If the problem says ... | Use |
|---|---|
"for each of the n items" |
for |
"repeat k times" |
for |
| "while there is something left", "until it becomes zero" | while |
| "until the input runs out" | while |
| "until it is found" | while or for + break |
Both are equally powerful — anything that can be written with
for can be written with while, and vice versa.
Pick the one that makes the intent read most clearly.
do ... while
The same as while, but the body runs at least once
because the condition is checked at the end:
int pilihan;
do {
cin >> pilihan;
} while (pilihan < 1 || pilihan > 3);This form is rarely used for online judge problems, but it is useful for interactive programs.
break and
continue
breakexits the loop immediately.continuejumps to the next iteration.
for (int i = 1; i <= 100; i++) {
if (i % 3 != 0) continue; // skip anything that is not a multiple of 3
if (i > 20) break; // stop as soon as we pass 20
cout << i << " ";
}3 6 9 12 15 18
Open
exercises/05/02-while.cpp.
Change the starting number to 0, recompile, and notice that
the digit count comes out as 0 — not 1. This
is an edge case that is often forgotten in problems.
The accumulation pattern
The most common pattern in this entire book: set up an accumulator before the loop, change its contents in every iteration, use the result after the loop.
long long total = 0; // accumulator
for (int i = 1; i <= n; i++) {
total += i; // changed every iteration
}
cout << total << "\n"; // used afterwardsThose three parts — set up, change, use — are always there. The part most often gotten wrong is "set up", because the initial value differs for each kind of accumulation:
| What is computed | Accumulator initial value | Operation inside the loop |
|---|---|---|
| sum | 0 |
total += x |
| product | 1 |
hasil *= x |
| count of matches | 0 |
banyak++ |
| largest value | the first element, or a very small number | if (x > besar) besar = x |
| smallest value | the first element, or a very large number | if (x < kecil) kecil = x |
An initial value of 0 for a product makes the answer
always zero; an initial value of 0 for "largest" makes the
answer wrong when all the data is negative. Both are classic mistakes
that pass the sample cases in the problem and are only caught during
judging.
Open
exercises/05/03-akumulasi.cpp.
Change long long faktorial = 1; to = 0,
recompile, and run it for n = 10. The sum and count lines
are still correct, but the factorial becomes 0 — one wrong
initial value is enough to break one answer.
Variations whose shape you should memorize:
// count how many satisfy a condition
int banyak = 0;
for (int i = 1; i <= n; i++) if (i % 7 == 0) banyak++;
// find the largest
int besar = -1000000000;
for (int i = 0; i < n; i++) { cin >> x; if (x > besar) besar = x; }
// running product (factorial)
long long hasil = 1;
for (int i = 2; i <= n; i++) hasil *= i;The accumulator for a sum should almost always be
long long. Adding up 100,000 numbers that are each up to
10⁹ gives 10¹⁴ — int overflows long before that.
Processing digits
Combining % 10 and / 10 from Chapter 03
inside a while gives a way to read a number digit by digit,
from the back:
long long n = 4071;
int jumlah = 0;
while (n > 0) {
int digit = n % 10; // take the last digit
jumlah += digit;
n /= 10; // drop the last digit
}
cout << jumlah << "\n"; // 12Nested loops
A loop may sit inside another loop. The inner one runs completely for every iteration of the outer one.
for (int baris = 1; baris <= 4; baris++) {
for (int kolom = 1; kolom <= baris; kolom++) {
cout << "*";
}
cout << "\n";
}*
**
***
****
How to read it: the outer loop determines how many rows, the inner loop determines the contents of one row. For the example above:
baris |
kolom runs from–to |
stars printed |
|---|---|---|
| 1 | 1 … 1 | 1 |
| 2 | 1 … 2 | 2 |
| 3 | 1 … 3 | 3 |
| 4 | 1 … 4 | 4 |
In total 1 + 2 + 3 + 4 = 10 stars. Also note where
cout << "\n" sits: it is inside the outer loop but
outside the inner loop. Moving it one level in puts
every star on its own line; moving it out puts everything on one long
line.
The amount of work multiplies too: two nested loops that each go up
to n do about n² steps. For n = 1000 that is a
million steps (fast), for n = 100000 that is ten billion
(far too slow). Chapter 10 covers how to estimate this before writing
code.
Open
exercises/05/04-bersarang.cpp
and change it so the triangle is upside down (the first row has four
stars, the last row has one).
The multiple test case pattern
Many contest problems give the number of test cases on the first line. The standard form:
int t;
cin >> t;
while (t--) {
int a, b;
cin >> a >> b;
cout << a + b << "\n";
}t-- gives the current value of t to
while, then decrements it by one. When t
reaches 0, the loop stops.
In problems with multiple test cases, every accumulator variable must
be reset inside the while (t--) loop, not
outside it. An accumulator set up once outside carries leftover counts
from the previous case into the next one — the first case's answer is
correct, the rest are wrong. This is the most common cause of partial
scores on multi-test-case problems.
Open
exercises/05/06-banyak-kasus.cpp.
Add int total = 0; as the first line
inside while (t--) and change the print
line to total += a + b; cout << total << "\n";.
Run it with the sample input at the top of the file; the output is
3, 30, 14. Then move
int total = 0; above the while and run again:
the output changes to 3, 33, 47
because the previous cases' counts are carried along.
When a loop will not stop
A program that hangs without printing anything almost always means one of three things:
- The value being checked never changes. Check whether there is a line inside the loop that actually changes it.
- The change moves away from the stopping condition.
For example
i--in a loop that waits forito reachn. - The program is waiting for input. Not an infinite
loop, but a
cin >>waiting for a number you never typed.
Stop a hanging program with Ctrl + C. To see what is
actually happening, temporarily insert
cerr << i << " " << n << "\n";
inside the loop — cerr is used, not cout, so
it does not pollute the judged output (Chapter 10).
Homework
Problem 1 — Sum from 1 to N
Open
exercises/05/homework/01-jumlah-n.cpp.
Read n (up to 1,000,000), print
1 + 2 + ... + n. Pay attention to the accumulator type.
Problem 2 — Digit sum
Open
exercises/05/homework/02-jumlah-digit.cpp.
Read one positive integer (can be up to 10¹⁸), print the sum of all its
digits. Example: 4071 gives 12.
Problem 3 — Multiplication table
Open
exercises/05/homework/03-tabel-perkalian.cpp.
Read n, print the n × n multiplication table.
Each row contains n numbers separated by a single space,
with no extra space at the end of the row.
Problem 4 — Number triangle
Open
exercises/05/homework/04-segitiga-angka.cpp.
Read n, print the following pattern for
n = 4:
1
12
123
1234
Challenge — Prime number test
Open
exercises/05/homework/05-prima.cpp.
Read one integer n (up to 10¹²), print PRIMA
("prime") or BUKAN ("not"). Checking every number from 2 to
n-1 is too slow; stop at the square root of n,
because if n = a × b then one of the factors is certainly
no larger than the square root of n. Use
for (long long i = 2; i * i <= n; i++). Do not forget
that n = 1 is not prime.
Most common mistakes
| What was written | Consequence |
|---|---|
for (int i = 0; i <= n; i++) on an array |
touches a[n], which does not exist |
for (int i = 0; i < n; i++); |
the semicolon closes the loop; the body runs once |
int accumulator for a large sum |
overflows into a negative number |
accumulator set up outside while (t--) |
the second and later test cases are wrong |
| no line that changes the condition | infinite loop, Time Limit Exceeded |
besar = 0 when the data can be negative |
answer is always 0 |
cout << "\n" at the wrong level |
output lines are jumbled |
OSN tips
The multiple test case pattern must be automatic.
Some OSN problems wrap one small problem in T test cases.
The form is always the same, and you should have it memorized:
int t;
cin >> t;
while (t--) {
// read one case, print one answer
}Two mistakes that often appear in this pattern: an accumulator variable that is not reset to zero at the start of each case, and answers that are collected first and printed at the end when the problem asks for one line per case.
Estimate the number of steps before writing code. The rough rule used throughout this book: the judge machine completes about 10⁸ simple operations per second. From that, the constraints in the problem directly tell you the shape of solution that fits:
Constraint N |
Loops that still fit in 1 second |
|---|---|
| ≤ 1,000 | two nested levels (N² = 10⁶) |
| ≤ 100,000 | one level, or N log N |
| ≤ 1,000,000 | one level only |
| ≤ 10¹⁸ | no loop over N at all |
A constraint of 10¹⁸ is a clear signal: what is wanted is a formula or a loop over its digits, not over its value.
Intermediate values can be much larger than the
input. A problem that states N ≤ 10⁶ does not
guarantee that every number in your program stays small. The Collatz
sequence below is an example: starting from 704,511, the sequence
reaches 56,991,483,520 — more than twenty times past
int.
A loop that does not stop is judged Time Limit
Exceeded, not an error. If your answer is correct on your
own computer but gets TLE on all test files, suspect a stopping
condition that is never reached — for example
while (n != 1) for an n that can be 0.
Output with many lines is better collected. For
problems that print hundreds of thousands of lines, printing with
endl in every iteration makes the program many times
slower. Use "\n".
for (int i = 0; i < n; i++) with n of
type long long equal to 3 × 10⁹ will never stop:
i of type int overflows into a negative number
before reaching n. Make the counter type match the bound
type.
Summary
foris used when the number of iterations is known,whilewhen only the stopping condition is known.- The three parts of
for: initialization (once), condition (before each iteration), update (after each iteration). for (int i = 0; i < n; i++)runsntimes and matches array indices; changing<to<=adds one iteration.- Accumulation pattern: set up the accumulator, change it every iteration, use it afterwards. The initial value differs for a sum, a product, and a largest value.
while (n > 0) { n % 10; n /= 10; }reads a number digit by digit from the back.- Nested loops do about n² steps — enough for
nin the thousands, not fornin the hundreds of thousands. - Accumulators in multi-test-case problems must be reset for every case.
OSN-style exercises
All three problems below are solved with loops only — no arrays needed yet. The third problem uses the multiple test case pattern you will often encounter in OSN.
Starter code for all three is in
exercises/05/osn/.
Problem 1 — Collatz Sequence
Take a positive integer N. As long as the number is not
yet equal to 1, apply the following rule repeatedly:
- if the number is even, divide it by two;
- if the number is odd, multiply it by three and add one.
One application of the rule counts as one step. Compute how many steps are needed until the number becomes 1.
Input format. One line containing one integer
N.
Output format. One line containing the number of steps.
Constraints. 1 ≤ N ≤ 10⁶. For every
N within these constraints, the sequence is guaranteed to
reach 1.
Sample input
6
Sample output
8
Explanation. The sequence is 6 → 3 → 10 → 5 → 16 → 8
→ 4 → 2 → 1, which is eight steps. For N = 1, the answer is
0 because the number is already 1 from the start.
Starter code:
exercises/05/osn/01-collatz.cpp
Problem 2 — Palindrome Number
A number is called a palindrome if its sequence of digits reads the same from the front as from the back. For example 12321 and 7 are palindromes, while 12345 and 10 are not.
Input format. One line containing one integer
N.
Output format. One line containing YA
("yes") if N is a palindrome, or TIDAK ("no")
if it is not.
Constraints. 1 ≤ N ≤ 10¹⁸. The number
must not be read as text.
Sample input
12321
Sample output
YA
Explanation. The digits of 12321 read from the back
are still 12321. Note the constraint 10¹⁸: a number that large does not
fit in int, and a loop running N times is
clearly impossible — what is repeated over is the digits, and there are
at most 19 digits.
Starter code:
exercises/05/osn/02-palindrom-angka.cpp
Problem 3 — Number of Factors
For each given number, count how many positive integers divide it evenly.
Input format. The first line contains one integer
T, the number of test cases. Each of the next
T lines contains one integer N.
Output format. T lines. Line
i contains the number of positive factors of N
in test case i.
Constraints.
- 1 ≤
T≤ 50 - 1 ≤
N≤ 100,000
Sample input
3
12
1
97
Sample output
6
1
2
Explanation. The factors of 12 are 1, 2, 3, 4, 6, and 12 — six of them. The number 1 has only one factor, itself. The number 97 is a prime number, so its only factors are 1 and 97.
Starter code:
exercises/05/osn/03-banyak-faktor.cpp
Confused, or already done? Open the homework solutions page.