Categories
Programming Tutorial

Union Day Challenge Tutorial

Tutorial

Click here to download the PDF of the problems or click here to see the challenge results.

Contents:

Problem A: The Tallest One

This problem requires a typical tracking of an optimum value (e.g. maximum or minimum) within a list. Generally, finding the maximum/minimum number from a given list involves visiting each number and remember the biggest/smallest you have encountered so far. This works for both sorted and unsorted lists.

For a sorted lists, the minimum and maximum can quickly be determined from the first and last items (depending on whether the list is sorted in descending or ascending order).

Given N numbers (heights in this case), we can maintain two variables: height that will temporarily store the value of each student’s height (one at a time), and maxHeight that will store the height of the tallest student found so far. At first, we can initialize maxHeight with the least possible height (i.e. zero). Note that zero or any negative number will work.

Then, repeating N times, we can input the next height and compare it with maxHeight. If it is greater, we will update maxHeight to be the value of this height. After comparing all N heights, maxHeight will have the tallest student’s height. This can be implemented as follows in C++.

// Get the count of total students
int N;
cin >> N;

// Find the tallest height
int height, maxHeight = 0;

for( int i = 0; i < N; i++ ){
    // Get the height of next student
    cin >> height;

    // Check if this height is taller than the current tallest
    if( height > maxHeight ) maxHeight = height;
}

// Display the tallest height
cout << maxHeight << endl;Code language: JavaScript (javascript)

Notice that we did not store all the numbers before finding the tallest height. This is because we do not need to remember all the heights, only the tallest height seen so far.

Problem B: Behind The Tallest One

Since the problem guarantees that there is only a single tallest student in the class, we can use the solution for Problem A and calculate the position of the tallest student (position). Then, the number of students behind the tallest students will be “N - position“.

// Get count of total students
int N;
cin >> N;

// Find the tallest
int height, maxHeight = 0, position;

for( int i = 0; i < N; i++ ){
    // Get the height of next student
    cin >> height;

    // Check if this height is taller than the current tallest
    if( height > maxHeight ){
        maxHeight = height;
        position = i + 1;
    }
}

// Display the count of students behind the tallest
cout << N - position << endl;Code language: JavaScript (javascript)

Similar to Problem A above, we did not store all the numbers before finding the tallest height. This is because we do not need to remember all the heights, only the tallest height seen so far (and its position).

Problem C: The Best Three

The problem involves selecting three adjacent scores at a time and check if they give the best total.

Since we are guaranteed to always have at least three scores, we can maintain an adjustable array (i.e. vector) of the scores. At first, we will add the initial three scores, at once, and assume they give the best total (it is the only triple so far).

Then, we will add one score at a time at the end of the list. After adding each score, we will check if the last three scores give a better total. Once we are done adding all numbers into the list, the best score will have been determined and we can display it.

// Get count of total students
int N;
cin >> N;

vector<int> scores(3); // Stores all scores in the class. At first, has space for the first three

// Get the first three scores
cin >> scores[0] >> scores[1] >> scores[2];

// Assume the first three scores are the better
int maxTriple = scores[0] + scores[1] + scores[2];
int pos = 2;

// Determine if there is a better triple
for( int i = 0; i < N-3; i++ ){
    // Add the next score into the list
    int score;
    cin >> score;
    scores.push_back(score);

    // Check if the last three scores are the best
    int p = scores.size() - 1;
    if( scores[p-2] + scores[p-1] + scores[p] > maxTriple ){
        maxTriple = scores[p-2] + scores[p-1] + scores[p];
        pos = p;
    }
}

// Display the best triple
cout << scores[pos-2] << " " << scores[pos-1] << " " << scores[pos] << endl;Code language: JavaScript (javascript)

Problem D: The Swaps

This problem at its core is a sorting problem. This is because at the end of the day, we need to make sure that each student stands behind a student who is shorter or the same height. The challenge is to sort the student in the minimum number of swaps possible.

The problem assumes that all students have different heights. Think of how you would modify this solution if students with duplicate heights were allowed.

There are many algorithms for sorting. Each algorithm has its advantages and disadvantages. Furthermore, some algorithms are useful for small lists (e.g. bubble sort, selection sort, and insertion sort) while others are preferable for large lists (e.g. merge sort, and quick sort). We recommend that you understand how to implement the bubble, selection, and insertion sorting algorithms.

Selection sort is the most useful for counting the minimum swaps. Selection sort arranges a list in a sorted order minimizing the number of swaps needed. Refer to this tutorial for a detailed explanation of how a selection sort works. We can implement the algorithm to solve the problem as follows.

// Get count of total students
int N;
cin >> N;

// Get all students heights
int heights[N];
for( int i = 0; i < N; i++ ) cin >> heights[i];

// Compute minimum swaps
int swaps = 0;
for( int range = N-1; range > 0; range-- ){
    // Determine the tallest within the current range
    int tallestPos = 0;
    for( int pos = 1; pos < range; pos++ )
        if( heights[pos] > heights[tallestPos] ) tallestPos = pos;

    // Move the tallest to the back of the range if he/she is not there
    if( heights[tallestPos] > heights[range] ){
        swaps++;
        int h = heights[range];
        heights[range] = heights[tallestPos];
        heights[tallestPos] = h;
    }
}

cout << swaps << endl;Code language: JavaScript (javascript)

Problem E: Mike’s Puzzle 2.0

Perhaps a good way to describe an approach to solving this problem is with an example. Suppose we have a grid below.

The objective is to find the shortest path that runs from the top left corner (0, 0) to the bottom right corner (4, 4).

Starting with the top left corner, we can calculate the distance of adjacent tiles, one at a time, until we reach the bottom right. Study the steps below.

First, we start by assigning the distance of the starting tile (in Step A). Then (in Step B), we calculate the distances of all tiles that are 1 step away from the starting tile (i.e. the tiles that are directly connected to the starting tile). Then (in Step C), we calculate the tiles are 2 steps away, since we already have the distances of all 1 step-away tiles. When there is more than one way to calculate the distance of a tile (e.g. the last tile in Step I), the shorter distance is selected because we are interested with the shortest distance. This continues until all paths have been completed (i.e. all reachable tiles have been visited).

This approach of processing all 1 step away tiles before going to the 2 steps away, and then process the 2 step away tiles before moving to the 3 steps away, and so on, is called breadth first search. This ensures that all correct shortest distances for tiles in step N have been computed before computing the distances of tiles in step N+1.

Technically, this method of calculating the shortest path between two points is called Dijkstra’s Shortest Path First algorithm (or simply Dijkstra’s algorithm).

We can simply implement that above algorithm as follows.

#include<iostream> // cin, cout, endl, pair, min
#include<cstdio>   // freopen, stdin, stdout
#include<climits>  // INT_MAX
#include<queue>    // queue

using  namespace std;

int main(){
    freopen("1.in", "r", stdin);
    //freopen("1.out", "w", stdout);

    // Get count of total students
    int N;
    cin >> N;

    // Get the grid
    int G[N][N];  // The grid
    int D[N][N];  // The distance of each tile from the beginning
    bool V[N][N]; // Tracks if a tile has been visited

    for( int x = 0; x < N; x++ ){
        for( int y = 0; y < N; y++ ){
            cin >> G[x][y];     // Get tile's weight
            D[x][y] = INT_MAX;  // Initialize tile distance
            V[x][y] = false;    // Initialize tile as not visited
        }
    }

    // Add the starting point into the queue
    queue< pair<int, int> > Q;  // Tracks reachable tiles that need to be visited
    Q.push(make_pair(0, 0));
    D[0][0] = G[0][0];

    // Compute the distance of all tiles reachable from (0,0)
    while( Q.size() > 0 ){
        // Remove the next tile from the queue
        pair<int, int> p = Q.front(); Q.pop();
        int x = p.first, y = p.second;

        // Check for a non-visited tile to the right
        if( y+1 < N && !V[x][y+1] && G[x][y+1] ){
            D[x][y+1] = min(D[x][y] + G[x][y+1], D[x][y+1]);
            if( !(x==N-1 && y==N-1) ) Q.push(make_pair(x, y+1));
        }

        // Check for a non-visited tile to the left
        if( y-1 >= 0 && !V[x][y-1] && G[x][y-1] ){
            D[x][y-1] = min(D[x][y] + G[x][y-1], D[x][y-1]);
            if( x!=N-1 || y!=N-1 ) Q.push(make_pair(x, y-1));
        }

        // Check for a non-visited tile below
        if( x+1 < N && !V[x+1][y] && G[x+1][y] ){
            D[x+1][y] = min(D[x][y] + G[x+1][y], D[x+1][y]);
            if( x!=N-1 || y!=N-1 ) Q.push(make_pair(x+1, y));
        }

        // Check for a non-visited tile above
        if( x-1 >= 0 && !V[x-1][y] && G[x-1][y]){
            D[x-1][y] = min(D[x][y] + G[x-1][y], D[x-1][y]);
            if( x!=N-1 || y!=N-1 ) Q.push(make_pair(x-1, y));
        }

        // Mark the current tile as visited if it is not the destination
        if( x != N-1 || y != N-1 ) V[x][y] = true;
    }

    cout << D[N-1][N-1] << endl;

    return 0;
}Code language: PHP (php)

Categories
Programming Tutorial

COVID-19 Challenge Tutorial

Tutorial

Click here to download the PDF of the problems or click here to see the challenge results.

Contents:

Problem E: Colony Structure

It’s the little details that are vital. Little things make big things happen.

John Wooden

This is a problem about patterns. The trick is to observe the patterns as N changes and figure out different sections making up the entire structure. The following is the structure when N = 10.

If we carefully consider a single row, say the fourth line, we can see that there are six dashes, followed by two hashes, followed by three pluses, followed by two hashes, followed by six dashes. The entire structure is made of rows where each row has five sections dashes, pluses, and hashes. The following image illustrates this and counts the elements in each section for each row.

When we divide a problem like this, it becomes easier to conquer individual pieces. We can clearly see the pattern for each section. For instance, from the first row to the last, left dashes decrease from 9 to zero and then increase to 9 again. In terms of N, this is decreasing from N-1 to zero and then increase to N-1 again. The same pattern applies to the right dashes.

The left hashes are simple to formulate their pattern. They are zero in the first and last rows and two for the remaining rows. Similarly, the right hashes are twos except for the first and last two rows where they are ones.

Lastly, the pluses are zeroes in the first and last two rows. From the third row, they increase in steps of twos, from 1 to the middle row. Then, they decrease in steps of twos back to 1.

We can implement the above patterns in the following nested loops. Note that there are many ways the above patterns could be implemented.

int N;
cin >> N;

for(int r = 1; r < 2*N - 2; r++){
    //
    for(int c = 0; c < abs(N-r-1); c++) cout << "-"; // Left dashes

    if(r > 1 && r < 2*N-3) cout << "##"; // Left hashes

    for(int c=0; c < 2*N-7 - 2*abs(r+1-N); c++) cout << "*"; // Pluses

    if(r > 2 && r < 2*N-4) cout << "##"; // Double right hashes
    else cout << "#"; // Single right hashes

    for(int c = 0; c < abs(N-r-1); c++) cout << "-"; // Right dashes

    cout << endl;
}Code language: JavaScript (javascript)
Categories
Programming Tutorial

Much-Match-March! Challenge Tutorial

Tutorial

Click here to download the PDF of the problems or click here to see the challenge results.

Contents:

Problem A: Babies Matching Game

This problem requires a typical application of an if-then-else block. After inputting the number (N) as an integer, the response can be evaluated as follows:

if( N % 15 == 0 ) cout << "Much-Match-March!" << endl;
else if( N % 3 == 0 ) cout << "March!" << endl;
else if( N % 5 == 0 ) cout << "Much!" << endl;
else cout << "Match!" << endl;Code language: JavaScript (javascript)

Notice how you need to check if the number is divisible by both 3 and 5 (line 1) before checking if it is divisible by only 3 (line 2) or only 5 (line 3).

Problem B: Teens Matching Game

This problem has two parts: (1) calculating the sum and product of individual digits of a number, and (2) compare which of the two quantities is greater.

Part (1) can be solved in a number of ways. One useful method is to see a number as a collection of digits. This means that we can first input the number as a string.

string N;
cin >> N;

Then, we can go over each digit of the number (which are now stored as characters of the string) and sum/multiply them. Before we can sum/multiply individual digits, we will need to convert them to numbers. This can easily be achieved by using the ASCII code values of the characters.

int sum = 0, product = 1;
for( int i = 0; i < N.size(); i++ ){
    sum += N[i] - '0';
    product *= N[i] - '0';
}Code language: HTML, XML (xml)

At this point, sum and product can be compared, which is Part (2) of the problem. This part is similar to Problem A above as well as this problem from Challenge 06.

Problem C: Adults Don’t Match

This problem is similar to problems A and B above. The unique challenge here is how to reverse the digits of the number. If we input a number as a string as follows.

string NS;
cin >> NS;

The reverse of the number in string can be computed by going over the digits of the number from its end to the beginning and adding the digits to the reverse number.

string RNS = "";
for( int i = NS.size()-1; i >= 0; i--){
    RNS += NS[i];
}Code language: JavaScript (javascript)

Then, we can convert the two strings to their integer equivalent using stoi() function.

int N = stoi(NS);
int RN = stoi(RNS);

Then the two quantities can be compared like in Problem B above.

Problem D: Vending Machine

This is a simulation problem similar to Mike’s Puzzle problem we solved in Challenge 06. Again, the challenge here is to track all the relevant data and simulate the operation of selling water bottles to students.

A good question to always ask is, what information needs to be tracked throughout the operation of the vending machine. Clearly, it is:

  • how many coins of each type (50, 100, and 200) are in the vending machine. This will help us compute total sales as well as if it is possible to return change to the next student.
  • how many bottles are left (since the problem wants us to assume that the vending machine will always have enough bottles, this will not be necessary to track).

We can use an array to track available coins.

int vm_coins[] = { 0, 0, 0 }; // 1st:50, 2nd:100, 3rd:200Code language: JavaScript (javascript)

Then, we can serve one student at a time. For each student, we will get his/her coin and how many bottles he/she wants to buy. If the student needs a change (e.g. has a 100-coin and wants a single bottle), then we can check in the coins tracker, if we have enough coins to return the change. If we can return the change, we will remove the coins that will be used to return the change and add the new coin. If we cannot return the change, then we can stop here, report as “NO”, and terminate the program.

If we can repeat the above for all students, then we will report as “YES”.

Note that, in this case, since only 50, 100, and 200 are used, change can only be needed for a student who has 100 or 200 coin. Hence, change that need to be returned can only be either TZS 50, 100, or 150. For this reason, the coins that are needed to return change are only 50 and 100. We will use this knowledge to quickly determine the coins to use to return a change.

Hence, suppose we store the total students in an integer, N, we can implement the above process with a simple loop:

while ( N-- ){
    // Get the coin and bottles for the next order
    int coin, bottles;
    cin >> coin >> bottles;

    // When change needs to be returned
    const int PRICE = 50;
    if ( PRICE * bottles < coin ){
        // Calculate the change
        int change = coin - PRICE * bottles; 

        // Compute if 100s are needed and available to be returned as change
        if( change >= 100 && vm_coins[1] > 0 ){
            change -= 100;
            vm_coins[1]--;
        }

        // Compute if the remaining change can be returned using 50s
        if( change >= 50 && vm_coins[2] >= change/50){
            vm_coins[2] -= change / 50;
            change = 0;
        }

        // When returning a change cannot be done
        if( change > 0 ){
            cout << "NO" << endl;
            return 0;
        }
    }

    // Add the coin to the vending machine
    vm_coins[coin==200 ? 0 : (coin==100 ? 1 : 2)]++;
}

cout << "YES" << endl;Code language: JavaScript (javascript)

Problem E: Fixing the Vending Machine

This problem is similar to Problem D above. However, in this case, we need to rearrange the students in the best way to serve them.

The question then becomes, “What is the best way to rearrange the students before serving them?”. If you think about it, the best approach is to serve all students who do not need change first. Then, we can serve those who need some change. We can achieve this by sorting the students from those with the smaller coin (50) to those with the largest coin (200).

Since we need to sort the students, we will need to store all of them (i.e. keep a list of each student’s coin and the number of bottles). C++ has a data structure called pair that stores two things as a single item. Each pair will belong to one student. We can store all the pairs in an array or vector. For the sake of learning, we will use a vector.

Hence, as a summary, we will get all students orders into a list. Then, we will sort them according to their coins (from smallest to largest). Then, we will try to serve the orders exactly as we did in Problem D above.

The above can be implemented as followed. A complete source code is provided to showcase a good way to write and organize source code. The source code is heavily commented on to help you follow along.

#include<iostream>  // pair, exit(), cin, cout, endl
#include<cstdio>  // freopen(), stdin, stdout
#include<vector>   // vector
#include<algorithm>   // sort()

using  namespace std;

int main(){
    // For easy debugging
    freopen("1.in", "r", stdin);
    //freopen("1.out", "w", stdout);

    // Get students count
    int N;
    cin >> N;

    // Tracks coins in the vending machine (1st:50, 2nd:100, 3rd:200)
    int vm_coins[] = { 0, 0, 0 };

    // Get all students orders
    vector<pair<int, int> > orders;
    while ( N-- ){
        int coin, bottles;
        cin >> coin >> bottles;
        orders.push_back(make_pair(coin, bottles));
    }

    // Sort students orders. By default, sorting pairs chooses the first item
    // in the pair, and also sorts the items from smallest to largest
    sort(orders.begin(), orders.end());

    // Try to serve each student's order
    for( pair<int, int> order : orders ){  // C++11 or above is needed for shorthand for loop
        int coin = order.first;  // Get coin from the current student's order
        int bottles = order.second;  // Get the number of bottles

        // When change needs to be returned
        const int PRICE = 50;
        if ( PRICE * bottles < coin ){
            int change = coin - PRICE * bottles; // Calculate the change

            // Compute if 100s are needed and available to be returned
            if( change >= 100 && vm_coins[1] > 0 ){
                change -= 100;
                vm_coins[1]--;
            }

            // Compute if the remaining change can be returned using 50s
            if( change >= 50 && vm_coins[2] >= change/50){
                vm_coins[2] -= change / 50;
                change = 0;
            }

            // When returning a change cannot be done
            if( change > 0 ){
                cout << "NO" << endl;
                exit(0);  // Terminate the program. Also, "return 0;" can be used
            }
        }

        // Add the new coin to the vending machine
        vm_coins[coin==200 ? 0 : (coin==100 ? 1 : 2)]++;
    }

    cout << "YES" << endl;

    return 0;
}Code language: PHP (php)
Categories
Best Practices Programming Tutorial

Best Practices Part 2: Speed Up Testing and Uploading

Best Practices for STEM Loyola Programming Challenges

Contents:

This tutorial assumes that you have already read Part 1: “Understand the Basics”.

Overview

Time is critical during a programming contest. Few seconds can be the difference between being the first or second. One of the time consuming parts during a programming challenge is repeatedly testing your code against the provided test cases. This tutorial will describe few tricks that you can use to test and upload your solution fast.

…a professional is someone who can do his best work when he doesn’t feel like it

Alistair Cooke

Filename Convention

The contest platform can help you populate most of the fields when you submit your solution. This happens when you name the file of your source code using the short code given for a particular problem.

The short code of problems is available in the title row where the color of a problem and its points are displayed. Usually, the short codes are A, B, C, D, and E.

If you are solving Problem A in C++, then naming your source code as A.cpp will automatically cause the website to select the problem and the your programming language after you browse for the source file. Few seconds saved there.

Redirect Standard Input/Output for Testing

When you write a solution to a problem, you will need to test it several times for correctness. This is a tedious task, especially when the test data is huge. Fortunately, feeding data to your program for testing can and should be automated.

The first method does not involve modifying your source code and it involves the use of a Terminal or PowerShell.

During an ongoing challenge, if you visit the Problem Set section, you can download the sample test cases. Click on the “zip with all samples” to download all sample test cases. After extracting, each sample test case will have its input and output file (e.g. 1.in and 1.out for test cases #1). Copy and paste the test cases into the same folder that has your program.

Open a Terminal (In Linux/MAC) or PowerShell (in Windows) and navigate to the folder containing your program using a change directory command (cd). In Windows, suppose your program is in “C:\Users\STEM\Documents\My Programs”, then you will type:

cd "C:\Users\STEM\Documents\My Programs"

In Linux/MAC, if your program is in “/home/stem/Documents/Programs”, then you will type:

cd /home/stem/Documents/Programs

At this point, if you list the contents of your directory (using ls command), you should be able to see your compiled program and the test cases.

By default, when you execute your program, the standard input is mapped to the keyboard. You will have to manually type in the test case data to check your program. However, you can map the standard input to the test case you want to test your program with. Suppose you are in Windows, and your program is called “A.exe”, and you want to run it using the data in “1.in”, you can type the following in the PowerShell.

cat 1.in | .\A.exe

Or you can also use the following command.

Get-Content 1.in | .\A.exe

Your program (A.exe) will be executed using the data in “1.in”. Results will be displayed in the PowerShell. Then, you can compare the results of your program with the sample test case one results in “1.out”. If you want to save the results of your program in a file (like “my-results.txt”), then you can add ” > my-results.txt” to either of the above commands.

cat 1.in | .\A.exe > my-results.txt

Adding ” > my-results.txt” redirects the standard output from being the PowerShell (screen) to the “my-results.txt” file.

Similarly, if you are using Linux/MAC, you can execute the above commands like:

cat 1.in | ./A > my-results.txt

You can omit the ” > my-results.txt” if you want the results to be displayed in the Terminal.

Redefine Standard Input/Output for Testing

This is another method of mapping the standard input/output to a file. This method involves modifying your source code but it is more convenient if you are using an IDE like CodeBlocks (Our recommended IDE for C/C++). We will demonstrate this method using C++.

This method uses the C function freopen found in the cstdio (or stdio.h) header. Hence, you need to include this header like:

#include<cstdio>

Then, at the top of your main() function, add the following line to use test data from “1.in” file.

freopen("1.in", "r", stdin);

When you compile and run your program (F9 in CodeBlocks), there will be no entering data manually as that will automatically be fetched from “1.in”. You are welcome!

At this point, the results of your program will be displayed on the screen. If you want to save them into a file, you can redefine the standard output to that file (say my-results.txt) using:

freopen("my-results.txt", "w", stdout);

A complete program that adds two numbers (taken from “1.in”) and displays results to the screen can be written as follows.

#include<iostream>
#include<cstdio>

using  namespace std;

int main(){
    freopen("1.in", "r", stdin);
    //freopen("my-results.txt", "w", stdout);

    int A, B;
    cin >> A >> B;

    cout << A + B << endl;

    return 0;
}
Code language: PHP (php)

You can comment out line 8 to have the results saved into “my-results.txt”.

Important Note: Remember to remove (or comment out) the lines that redefine the standard input/output (lines 7 and 8 in the above source code) before uploading your solution to the contest platform.

Using Java, the above can be written as follows:

import java.util.*;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.File;
import java.io.PrintStream;

public class A {
    public static void main( final String[] args) {
        try {
            System.setIn(new FileInputStream(new File("C:\\Users\\stem\\Documents\\My Programs\\1.in")));
            //System.setOut(new PrintStream(new File("C:\\Users\\stem\\Documents\\My Programs\\my-results.txt")));
        } catch (FileNotFoundException e) {
            System.out.print(e.getMessage());
        }        

        Scanner input = new Scanner(System.in);

        int A = input.nextInt();
        int B = input.nextInt();
        
        System.out.println(A + B);
    }
}
Code language: JavaScript (javascript)

Important Note: Remember to remove (or comment out) the lines that redefine the standard input/output (lines 11 and 12 in the above source code) before uploading your solution to the contest platform.

Stay tuned for Part 3 of this series and all the best tackling the next challenge!

Categories
Best Practices Programming Tutorial

Best Practices Part 1: Understand the Basics

Best Practices for STEM Loyola Programming Challenges

Contents:

Overview

This is a multi-part tutorial on participating in the STEM Loyola Programming Challenges. The objective of this series of tutorials is to equip you with the necessary tools to become competitive as you participate in future challenges.

Give me six hours to chop down a tree and I will spend the first four sharpening the axe

Abraham Lincoln

This series will highlight the tools that you will need such as programming languages, data structures, algorithms, and other principles/tricks. In this part, we will provide an overview of the automatic challenges grader and a good method of approaching solving the problems.

Understand the Automatic Challenges Grader

Since all programming challenges are automatically graded, it matters to understand the system. The grader works by accepting the source code you upload (e.g. file.cpp, file.java, file.c), compile the source code, execute the program, supply it with inputs, and observe if the program produces the correct output.

Suppose a problem requires you to input two positive integers less than a million and compute the sum of the two numbers. One of the correct ways to solve this problem is shown in the C++ source code below.

#include <iostream>
<iostream>using namespace std;

int main(){
    int A, B;
    cin >> A >> B;

    cout << A + B << endl;

    return 0;
}</iostream>Code language: PHP (php)

Note the following from the above source code:

  1. When inputting the two number into the program, we did not have to request the compiler to input the two numbers like cout << Enter the two numbers: ";
    This is because the compiler will automatically know to supply the two numbers and more importantly, anything you display on the standard output will be treated as your answer (resulting in the grader marking your program output as wrong).
  2. Similarly, when outputting the sum of the two numbers, we only displayed the sum without additional words like cout << "Sum is: " which will cause the grader to mark your answer as wrong. Because if the two numbers are 5 and 6, their sum is “11” and not “Sum is: 11
  3. The program ends with return 0;. Your program should run only once and return zero at the end. Ending your program with return main(); will result in time-limit error.
  4. Since the problem states that the two numbers are positive and less than a million, we chose int because we are sure a million can fit in the int data type. If the two numbers were bigger (say 5 billion), then we would have to select a bigger data type like long long int. Refer to our Programming and Data Structures in C++ guideline for an in-depth treatise of data structure.

Think of Problem’s Special Cases

Your submitted solution will be tested against five or more different test cases. Different test cases are used to ensure that your program has covered different special cases.

For instance, suppose the problem is to input two numbers and find the quotient when the first number is divided by the second. A basic solution can look like the source code below.

#include<iostream>

using namespace std;

int main(){
    float A, B;
    cin >> A >> B;

    cout << A / B << endl;

    return 0;
}
Code language: PHP (php)

However, the above program will crash when the second number is zero. This is due to the fact that in Mathematics, dividing a number by zero is undefined. What will happen exactly during your program’s execution depends on the compiler that was used, some compilers have protection against such cases and will produce inf as the answer. Some compilers will just result in run-time error and your program crashing.

To become a better programmer is to always think of such special cases that may crash your program and write your code defensively. A better solution for the above problem is as follows.

#include<iostream>
#include<cmath>

using namespace std;

int main(){
    const float INFINITESIMAL = 0.000001;
    float A, B;

    cin >> A >> B;

    if( abs(B) < INFINITESIMAL ){
        cout << "Cannot divide by zero!" << endl;
    } else {
        cout << A / B << endl;
    }

    return 0;
}Code language: PHP (php)

Note the following from the above solution:

  1. Since the numbers are assumed to be decimal numbers, line 12 does not check if the second number is zero using if( B == 0 ) because that works reliably with whole numbers (e.g. int, long, short, etc.) and not with decimal numbers (e.g. float and double). To check if a decimal number is zero, you select your precision (the smallest number that anything less than that will be assumed to be practically zero). Then compare if the magnitude of your number is less than that.
  2. In line 13, during an actual challenge, only write “Cannot divide by zero!” if the problem has requested you to do so.

Can I Do Better?

After crafting your solution, you should always ask yourself, “can I do better?

With the data structures that you have thought of using, can you do better? Is there a better data structure that you can use to make implementing the solution easier, faster, or use less memory? Do you really need to store all those numbers? Can you do better?

With the algorithm you have selected, is there a better one? Do you need to do something else (e.g. reverse the list, sort the list, find the binary representation of a number, etc.) before applying my algorithm? Can you do better?

You will be amazed by how much you can improve your solutions if you keep asking yourself this question and put in the effort to improve your solutions.

Part 2 “Speed Up Testing and Uploading” is now available.

Categories
Programming Tutorial

Welcome-Back! Challenge Tutorial

Tutorial

Contents:

Now we have the final results of the Welcome-Back Challenge, it is time to review and discuss the problems and their solutions in detail. There is always more than way to implement a solution in programming. We often speak of a better solution/algorithm as the one that requires less memory or execution time. Also, a better algorithm will perform fairly well on a small input as with the large input i.e. if it takes the algorithm 1 second to find a solution for 10X10 grid, it should not take a day to find the solution for 1,000,000X1,000,000 grid.

Click here to download the PDF of the problems.

Note: All the source code snippets will be provided in C++. The provided code snippets are examples of many ways to solve the problems.

Problem A: Decimal Part

The problem had two key challenges: how to extract the fraction part of a decimal number and how to format a number to display only four decimal places.

  1. Extracting a fraction part of a decimal number
    This can be achieved by finding the difference between the decimal number and the whole/integer part of the number.
float fraction = number - int( number );
  1. Display only four decimal places of a number
    In C++, the number of decimal places can be fixed using Input/Output Manipulation header (#include<iomanip>) tags: fixed and setprecision as shown below.
cout << fixed << setprecision(4) << fraction << endl;

Problem B: Gotcha!

This problem requires the application of a typical if-then-else block. After inputting the two numbers (say A and B), the sum and product of the two numbers can be compared as follows.

if( A+B > A*B ) cout << "SUM" << endl;
else if( A+B < A*B ) cout << "PRODUCT" << endl;
else cout << "GOTCHA!" << endl;

Problem C: Kiosk Change

This is an example of a problem that utilizes a greedy algorithm.

A greedy algorithm, as the name suggests, always makes the choice that seems to be the best at that moment. This means that it makes a locally-optimal choice in the hope that this choice will lead to a globally-optimal solution. Source

The minimum number of notes/coins can be determined by always asking the question: What is the biggest note/coin that can fit the current change?

Suppose that we need to return the change of TZS 3,500. The biggest note/coin we can use at the moment is 2000-note. After this, the remaining change will be TZS 1,500. At this point, the biggest note/coin is 1000-note. This will leave TZS 500 which we can use a 500-coin for. Hence, a TZS 3,500 change can be effectively be returned using 3 notes/coins.

Therefore, for any given change, we can walk through the given list of notes/coins (10000, 5000, 2000, 1000, 500, 200, 100, and 50) in descending order and check if the current note can be used to return the remaining change. Suppose the change is in variable change, the above algorithm can be implemented to produce the results in variable notes_needed.

const int DENOM_COUNT = 8;
const int DENOMS[] = { 10000, 5000, 2000, 1000, 500, 200, 100, 50 };

int notes_needed = 0;
while( change > 0 ){
    for( int i = 0; i < DENOM_COUNT; i++ ){ 
        if( change >= DENOMS[i] ){ 
            notes_needed += change / DENOMS[i]; 
            change %= DENOMS[i];  
        }
    }
}

Problem D: Usernames

The objective of this problem is straight forward. We have to go through the list of names and track every time a name is repeated. The challenge is how to maintain a unique count of usernames, and display the total number of unique names before printing the entire list of modified names.

It is always a good practice to ask the question: Is there any data structure that I can leverage?

For tracking a unique list of elements both a set and a map can be used in C++. Since the problem requires to track how many times a username has already been used, a map will be more desirable. Refer to Programming and Data Structure in C++ for a detailed explanation and examples on maps and sets.

Also, the problem requires us to print the total number of unique names before listing the modified names. This suggests that we will have to store the list of modified names somewhere before printing them. There are several ways to accomplish this in C++, here we shall use a string stream.

// Get the total count of usernames
int N;
cin >> N;

stringstream output;  // Stores modified names
map<string, int> uniqueNames;  // Tracks unique names and how many times they are used
string name;

// Process the list of names
while( N-- ){
    cin >> name;

    uniqueNames[name]; // Add the name with a count of zero if it's not in the list

    if( uniqueNames[name] == 0 ){  // First time to encounter the name
        output << name << endl;
        uniqueNames[name] = 1;
    }else{  // The name has already been used
        output << name << ++uniqueNames[name] << endl;
    }
}

// Display the results
cout << uniqueNames.size() << endl;
cout << output.str();Code language: PHP (php)

Problem E: Mike’s Puzzle

The objective of problem E is to trace a path from the top left corner to the bottom left corner while counting the number of visited cells and the direction changes made.

The problem makes this easier since it has guaranteed that the path has neither loops nor branches. This means there is only a single path between the top left and bottom right corner.

This is an example of a simulation problem. As Jonathan described below when answering the question “What does it mean by ‘simulation type problem’ in competitive programming?” in Quora:

It means that the main difficulty in the problem is not figuring out how to solve it conceptually, but instead writing code that follows the (usually complex) set of rules they tell you. The challenge is writing this code quickly and accurately. It is called a “simulation” problem because your code is simulating whatever situation is described in the problem. Source

Jonathan Paulson in Quora

Clearly an array will be a great data structure to use (specifically, an N by N array where N refers to the provided size of the grid). Then, we can fill the array with the ones and zeros.

Before we can start to navigate through the array, we need a way of marking the cells that we have already visited. Since we need to visit the grid only once, we can clear the cell (i.e. change the one to zero) once we visit it. This will simplify tracking the visited cells.

// Get the grid size
int N;
cin >> N;

// Construct and fill in the grid
int grid[N][N];
for(int x = 0; x < N; x++)
    for(int y = 0; y < N; y++)
        cin >> grid[x][y];

int dirChange = 0;  // Track how many times direction changes
int tiles = 1;      // Count how many tiles were touched

enum { NOT_SET, UP, DOWN, RIGHT, LEFT } dir = NOT_SET; // Define and track the direction

// Walk through the grid starting from the top left corner
int x = 0, y = 0;   // Current position

while( x != N-1 || y != N-1 ) {  // Stop when reaching the bottom right (N-1, N-1)
    if( x+1 < N && grid[x+1][y] ){   // Check if can go down
        grid[++x][y] = 0; // Clear the tile from the path
        if( dir != NOT_SET && dir != DOWN ) dirChange++; // Track if direction changed
        dir = DOWN; // Assign the current direction
    }else if( x-1 >= 0 && grid[x-1][y] ){ // Check if can go up
        grid[--x][y] = 0; // Clear the tile from the path
        if( dir != NOT_SET && dir != UP ) dirChange++; // Track if direction changed
        dir = UP; // Assign the current direction
    }else if( y+1 < N && grid[x][y+1] ){ // Check if can go right
        grid[x][++y] = 0; // Clear the tile from the path
        if( dir != NOT_SET && dir != RIGHT ) dirChange++; // Track if direction changed
        dir = RIGHT; // Assign the current direction
    }else if( y-1 >= 0 && grid[x][y-1] ){ // Check if can go left
        grid[x][--y] = 0; // Clear the tile from the path
        if( dir != NOT_SET && dir != LEFT ) dirChange++; // Track if direction changed
        dir = LEFT; // Assign the current direction
    }
    tiles++; // Increment visited tiles
}

cout << tiles - dirChange << endl;Code language: JavaScript (javascript)