Tuesday, December 30, 2008

Finding the easy problems

For starters, it would be best to try to solve the easier problems first. If one tried solving the hard problems first, then he might get frustrated immediately from getting rejected submissions one after the other. It could also happen that he would get stuck in a single problem for several days and end up extremely disappointed in programming and swearing never to do programming again.

For competitors in a contest, it is also best to find, solve and submit the easier problems first given a set of problems. Otherwise, one might spend so much time in a single hard problem that he losses time that could have been more productively used to submit much easier problems. So instead of submitting 3 easy problems at the end of the contest, he only submits 1 hard problem. But the weight of the easy problem is just the same weight as a hard problem in a programming competition.

So, when one starts a contest, before solving any problem, he should scan and sort out which problems are easier to solve first. Then he should solve the easier problems first. If he wishes to win, then he should resist the temptation of solving the hard problem first for whatever reason. The easy problem and the hard problem would earn one the same amount of money if the contest awarded cash prizes. So why should one consume all his time in solving a hard problem, unless he is not after winning the contest and just simply after programming insight enlightenment?

In this post, I discuss about finding easy problems in the UVa problem archive. Easy problems are only relative in the sense that they are easy compared to the other problems also existing within the archive. So how do we define relativity? Through statistics.

Figure 1 shows a screenshot of the problem archive where statistics are shown for each problem.

Figure 1. Screenshot of the problem archive.

Two ratio statistics are shown for each problem. The first one is the ratio of accepted submissions to total submissions. Let’s call this the BR ratio. For example, for problem 10800, the BR ratio is 19.73%. The second ratio is the ratio of number of users who actually solved the problem to the total number of users who attempted to solve the problem regardless of whether they solved it or not. Let’s call this the UR ratio. For problem 10800, the UR ratio is 67.51%.

Two other statistics exist which are the total submissions for a problem (B value) and the total number of users who attempted to solve a problem (U value). For problem 10800, B is 5039 meaning there were 5039 submissions for this problem whether the submissions were accepted or rejected. For the same problem, U is 1265, meaning there were 1265 users who submitted solutions whether their solutions were accepted or not.

So given this statistics, how do we interpret them? Generally, to find an easy problem, we would want to look for one which has a relatively high BR ratio. This means that an easy problem would be a problem for which most submissions were accepted because it was easy to solve so most people solved it in their first attempt or submission.

Figure 2 shows an example were an easy problem is visually spotted out.

Figure 2. Problem 10865 is likely to be an easy problem.

In Figure 2, one can easily point out that problem 10865 can likely be an easy problem because of the high BR ratio relative to its surrounding neighbor problems. The BR ratio stands out at 71%. Its closest rival has a BR ratio of 55.81%. The 71% BR ratio means that for about every 10 submissions, 7 of those were accepted. With a higher statistical historical ratio, one’s likelihood of solving the problem becomes higher. So it would be better if one start with such easy problems when starting his career in programming in order not to get frustrated at the start of his career immediately.

There is a watch out though. Figure 3 shows an example.

Figure 3. Problem 717 appears to be an easy one but could be actually a real hard one.

In Figure 3, problem 717 stands out easily with a BR ratio of 83.33%. This ratio is very high compared to other problems’ ratios. But looking at its B value of only 18, something looks fishy. The B value is very low which means that out of the tens of thousands of coders, only 18 solutions were submitted. Looking at the U value reveals something even more dismal: only 12 users attempted to solve this problem.

So why did most users avoid this problem as only 12 out of the tens of thousands of coders attempted to try this problem? Most probably this problem is a difficult one which most coders have no idea on how to solve. And those who attempted to solve it were most likely the genius type of users. Remember in high school or college when there would always be someone in class who stands out because of his extreme intelligence. Well, this is perhaps the type of person who solves the problem like problem 717.

So one feel that he is confident to take on really challenging problems, and that most problems have become too easy already, and that he has had enough programming experience already so that he is no longer vulnerable to becoming frustrated whenever he is unable to solve difficult problems, then one must at all cost avoid this type of problems with high BR ratios but extremely low B values.
Notice also that for problem 717, out of the 12 users who attempted it (U = 12), all of them were able to solve it (UR = 100%), confirming that these few people must indeed be geniuses.

Monday, December 29, 2008

UVa problem 11364: sample algorithm

To solve UVa problem 11364 (Parking), it would be beneficial to know that the minimal distance Michael has to walk is simply twice the distance from the farthest store to the nearest store. For example, consider the following setup:
A________B______________C_____D
X______________________________

A, B, C and D are the stores while X is Michael’s car. To get to all the stores and return to his car, Michael would have to walk:

A to B
B to C
C to D
D to C
C to B
B to A

In essense, the total distance he would have traveled would be just twice the distance from A to D.

Consider now the following setup:

A________B______________C_____D
__________________X____________

For Michael to get to all stores and return to his car, he would need to walk:

X to B
B to A
A to B
B to X
X to C
C to D
D to C
C to X

Again, analyzing this series of walks, it is simply twice the distance from A to D.

Hence to compute the minimal distance given a series of stores to walk to, simply find the nearest store and the farthest store, get their difference and then multiply the difference by 2.

UVa problem 11364: sample code

`001 #include "stdio.h"002 #include "stdlib.h"003 004 int main() {005   char  line[100]006       , ctmp[100]007   ;008   int   num_tests009       , num_stores010       , stores[100]011       , itmp012       , max013       , min014       , diff015       , i016       , j017       , k018       , m019   ;020 021   fgets(line, 100, stdin);022   sscanf(line, "%d\n", &num_tests);023 024   for (i = 0; i < num_tests; i += 1) {025     fgets(line, 100, stdin);026     sscanf(line, "%d\n", &num_stores);027 028     fgets(line, 100, stdin);029     for (j = 0, m = 0; 1; j += 1) {030       if (line[j] == '\n' || line[j] == 0) {031         break;032       }033 034       if (line[j] == ' ' || line[j] == '\t') {035         continue;036       }037 038       for (k = 0; 1 ; j += 1, k += 1) {039         if (line[j] == ' ' || line[j] == '\t' || line[j] == '\n' || line[j] == 0) {040           break;041         }042         ctmp[k] = line[j];043       }044       ctmp[k] = 0;045 046       stores[m] = atoi(ctmp);047       m += 1;048     }049     050     max = 0;051     min = 99;052     for (j = 0; j < num_stores; j += 1) {053       if (stores[j] > max) {054         max = stores[j];055       }056 057       if (stores[j] < min) {058         min = stores[j];059       }060     }061     diff = max - min;062     diff *= 2;063 064     printf("%d\n", diff);065   }066 067   return 0;068 }`

UVa problem 10851: sample algorithm

In UVa problem 10851 (2D Hieroglyphs decoder), one could explore the advantage of using right bit-shifts instead of long-cut division for division by powers of 2. For example, to divide a number by 128 (which is 2 raised to the power of 7), one could simply shift the number 7 bits to the right.Definitely, this fact could give one an advantage as a bit-shift operation is so much simpler and faster than traditional division.

Moreover, the mod operation may be quite computationally expensive as well. Since a mod by 2 operation is only needed, one could simply check whether the last binary bit of a number is 0 or 1 in order to tell if the number is divisible by 2 or not.

For example, the number 15 when represented in binary is 1111. Since its last bit (the right-most) is 1, then it is not divisible by 2. The number 12 when represented in binary is 1100. Since the last bit is 0, then it is divisible by 2. Bitwise-ANDing the number with 1 will enable one to get the last bit of the number.

For this problem, one could build a lookup table for each of the 256 ASCII characters. One could explore the use of a C++ standard map, where the key is the encrypted character and the value is the actual ASCII character. For example, we could build a map like the following:

Key, Value
//\\//\/, L
\/////\/, A

And so when our program sees an encryption of //\\//\/, it can simply lookup the map and find that the encryption stands for the ASCII character L.

UVa problem 10851: sample code

`001 #include "stdio.h"002 #include "string"003 #include "map"004 #include "iostream"005 006 using namespace std;007 008 int main() {009   char   line[100]010        , ctmp[100]011        , dtmp[100]012        , input[10][100]013        ;014   int   num_msg015       , i016       , ii017       , j018       , k019       , b020       , c021       , m022       , n023       , p024       , msg_len;025       ;026 027   map imap;028   map::iterator iter;029 030   for (c = 0; c < 256; c += 1) {031     ctmp[0] = '/';032     ctmp[9] = '/';033 034     for (i = 1; i < 9; i += 1) {035       ii = i - 1;036 037       b = c >> ii;038       b &= 1;039 040       ctmp[i] = b == 0 ? '/' : '\\';041     }042 043     ctmp[10] = 0;044 045     imap.insert(pair(ctmp, c));046   }047 048   fgets(line, 100, stdin);049   sscanf(line, "%d\n", &num_msg);050 051   for (i = 0; i < num_msg; ++i) {052     for (j = 0; j < 10; ++j) {053       fgets(line, 100, stdin);054       sscanf(line, "%s\n", &input[j]);055     }056 057     fgets(line, 100, stdin);058 059     msg_len = strlen(input[0]) - 2;060 061     for (m = 1, p = 0; m <= msg_len; m += 1, p += 1) {062       for (n = 0; n < 10; n += 1) {063         ctmp[n] = input[n][m];064       }065       ctmp[n] = 0;066 067       iter = imap.find(ctmp);068       dtmp[p] = iter->second;069     }070     dtmp[p] = 0;071 072     printf("%s\n", dtmp);073   }074 075   return 0;076 }`

Getting input

For programming competitions, input will usually come from standard input; that is, input is taken from the user manually typing the input, or input is directed into the program from a file.

When input is manually typed by the user, it will be like Figure 1.

Figure 1. Input manually typed by the user.

In Figure 1, the user manually types the input “User is typing this line now.”

When input is directed into the program from a file, it would appear like Figure 2.

Figure 2. Input directed from a file.

In Figure 2, the contents of the file i.txt are inputted into the program.

Reading a line from standard input can be done using Code 1.

Code 1. Reading a line from standard input.

#include "stdio.h"

```int main() {__char line[100];
__fgets(line, 100, stdin);
__return 0;}```

In Code 1, the fgets statement is configured to read from standard input using the stdin parameter. The line read is then placed into the character array line. The parameter 100 is used to indicate the maximum number of characters to be read. We use 100 since we declared the line variable to be a character array of 100 slots.

In Code 1, once the fgets statement is executed, the user can pretty much do anything with the line variable which contains the line read from standard input. For example, the user can print the contents into standard output such as the code in Code 2.

Code 2. Printing what was read from standard input.

#include "stdio.h"

int main() {

__char line[100];

__fgets(line, 100, stdin);

__printf("User typed the following: %s\n" , line);

__return 0;

}

When Code 2 is executed, it behaves like Figure 3.

Figure 3. Sample execution of Code 2.

Sunday, December 28, 2008

Dev-C++

Most of the algorithms I will be discussing will be based on how I will implement them in the C/C++ language. Although algorithms should be purely independent of the programming language used, I often think of the algorithms in terms of the underlying language I would be using to implement them.

In this post, I will be discussing the tool used for compiling the C/C++ codes I create to test my algorithms. Although everyone has his own way of compiling – like others may use g++ or Visual C++ – others may have no idea on how to compile. This post is dedicated to those others who have no idea or are confused on how to compile their codes. I present my way of compiling.

To compile my C/C++ codes, I use the tool Dev-C++. You can use Google to search for it. It’s a tool my teacher from school taught me. I thought I would only use it in school, but when I started working as an employee, I was also using it in the company’s regular programming competitions.

Interestingly, several years since I last used the tool in school, the tool had not changed much. Somehow, someway or another, people just stopped developing the tool, and so no new updates or versions of the tool were released since I last used it in school. And that was several years ago.

Somehow also, it is the only Windows C++ compiler I know how to use until this date. In school, I used to use Borland C++ or Microsoft Visual C++. But somehow, these other tools lost popularity. Borland C++ definitely lost popularity as I don’t hear anybody using it anymore, at least in my neighborhood. Microsoft Visual C++ definitely is ultra popular, but how in the world do you get the software for free. Microsoft keeps collecting money from us, until we third world developers have nothing left to use to develop our ideas and algorithms.

So, I was left with Dev-C++, until now, and who knows, maybe until I’m real old and shriveled like a rotten tomato, still aspiring to be a decent programmer.

Now, going back to the real topic. The Dev-C++ website is kind of confusing. I always got lost in it back in college when I had to download the binary and install it into a workstation. In my days, we had no personal laptops and so we had to hop from one computer to another, and install the bloody tool every time we saw that the workstation we hopped into did not have the tool installed into it.

http://www.bloodshed.net/devcpp.html

Figure 1 shows a screenshot of the site.

Figure 1. Screenshot of the Dev-C++ website.

Once installed, you can immediately use it. Figure 2 shows a screenshot of the Dev-C++ tool.

Figure 2. Screenshot of the Dev-C++ tool.

Through the tool, you type in your code, save it and then compile (CTRL-F9). Should there be no compile errors, you can close the dialog box shown in Figure 2. Open a command prompt and run the executable as shown in Figure 3.

Figure 3. Executing the executable.
In Figure 3, go to the directory where you saved your code. More often than not, the executable will be the same name as the file name of your code except for the suffix extension. For example, if you named your code a.cpp, then most likely, an executable named a.exe will be created if your compilation yielded no errors. Simply type the name of the executable to execute.

UVa Online Judge

The UVa Online Judge (http://icpcres.ecs.baylor.edu/onlinejudge/) is a website that archives thousands of problems used for computer programming competitions. It also provides a facility for validating code submissions to the problems published. Hence, it offers an excellent venue for testing algorithms.

Each problem in the archive is tagged with an ID number for easy identification. Refer to Figure 1.

Figure 1. Problems listing in the archive.

In Figure 1, the problem “Moscow time” has an ID number of 505.

Codes can be submitted for online validation can be written in C, Java, C++ or Pascal. Figure 2 shows the form for online submission.

Figure 2. Form for online code submission.

To search for a problem, one can opt to use the Google search bar as shown in Figure 3.

Figure 3. Google bar for searching problems.
Simply type in the problem ID in the Google bar. Be sure to tick the icpcres.ecs.baylor.edu button.

Welcome!

Welcome to Algorithm Share!

This blog is about sharing algorithms with others.

As for now, most of the problems to which algorithms will be discussed will come from the UVa Online Judge: http://icpcres.ecs.baylor.edu/onlinejudge/

I encourage you to post comments if you have algorithms to share as well. Most of the algorithms I will share will be my own ideas, but I believe that there are a thousand more algorithms out there that are more optimized than my own for a given problem. So, please do share your algorithms for me to learn as well.