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 two

The 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";   // 4

If 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

  • break exits the loop immediately.
  • continue jumps 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 afterwards

Those 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";   // 12

Nested 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:

  1. The value being checked never changes. Check whether there is a line inside the loop that actually changes it.
  2. The change moves away from the stopping condition. For example i-- in a loop that waits for i to reach n.
  3. 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

  • for is used when the number of iterations is known, while when 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++) runs n times 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 n in the thousands, not for n in 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.