tag:blogger.com,1999:blog-30185716079278975172019-06-04T02:00:28.164-07:00Anirudh Rayabharam's personal blogAlgorithms. Programming. Hacking.Unknownnoreply@blogger.comBlogger7125tag:blogger.com,1999:blog-3018571607927897517.post-70604147744511514132015-09-14T11:34:00.000-07:002015-09-14T11:37:21.129-07:00Setting up nginx + HHVM (hacklang) on OpenShiftI have recently moved my personal website from Google App Engine to <a href="https://openshift.redhat.com/" target="_blank">OpenShift</a>. I was looking at the list backend technologies supported by OpenShift and HHVM piqued my interest. I remembered how Facebook introduced a new language a few years ago and I never tried it out. So I decided to give it a try now. Fortunately, there is an OpenShift quickstart available for HHVM and we don't need to do any complex configuration to set it up.<br /><br />First of all, you need to sign up for OpenShift (it's free!). Now, that you have an OpenShift account, go ahead and create a new application. You will be shown a list of popular cartridges and quickstarts available. Select the HHVM quickstart. The next page will ask more details about your application like the sub-domain name, scaling options etc. Fill in the details as you wish and create the application.<br /><br />Now that your application is ready, you need a way to make changes to it. For that, you need to install rhc (OpenShift client tools) and manage your source code with git. Please refer to OpenShift's help section for a detailed explanation.<br /><br />At this point, I faced a problem. I added some Hack code and pushed to the server. To my surprise, it didn't work as expected. In fact, the page displayed nothing! On the contrary, PHP code worked fine. So, I logged into the application gear and examined the HHVM log file. The following error was being thrown:<br /><blockquote class="tr_bq"><code>Hack typechecking failed: typechecker command not found</code></blockquote>For some reason, the typechecker command <code>hh_client</code> is unavailable in the current path. I wasn't able to figure out why this was so but I figured out a workaround for this problem. We can fix it by disabling automatic typechecking. Add the following line to <code>config/hhvm.d/config.ini.erb</code> in your application code repository:<br /><blockquote class="tr_bq"><code>hhvm.hack.lang.auto_typecheck = false</code></blockquote>This will disable automatic typechecking which means that before pushing your code to the server, it is advisable to run typechecking on your local machine. Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-3018571607927897517.post-85269648036553064082015-02-22T08:37:00.000-08:002015-02-22T08:39:00.074-08:00Introduction to tries through Facebook Hacker Cup 2015 Round 1 - AutocompleteIn the <a href="http://blog.anirudhrb.com/2015/02/problem-discussion-facebook-hacker-cup.html" target="_blank">last week's post</a> I discussed the solution to the problem "Homework" from Facebook Hacker Cup 2015 Round 1. This post is about the problem "Autocomplete" from the same round.<br /><div><br /></div><div>You can read the <a href="https://www.facebook.com/hackercup/problems.php?pid=313229895540583&round=344496159068801" target="_blank">problem statement</a> available on the Facebook website. If you don't have a Facebook account, you can download problem statements from <a href="http://codeforces.com/gym/100579/attachments/download/3057/2015-fhc-r1.doc" target="_blank">Codeforces Gym</a>.<br /><br />I will re-state the problem in fewer lines. You have a new phone which has an auto-complete feature. But, it will complete your word only if there is there is no ambiguity on how the word should be completed. Formally, it will complete a word iff there is exactly one way to complete the word. It so happens that the phone's dictionary is empty by default. So, for the auto-complete feature to work, before typing a word you need to add that word to the dictionary. You need to send a message containing \(N\) words. What is the minimum number of characters must you type to send all \(N\) words? (Note that the typing involved in adding a word to the dictionary is not counted.) You need to solve \(T\) instances of this problem.<br /><br />Here are the constraints: <br /><ul><li>\(1 \le T \le 100\)</li><li>\(1 \le N \le 100,000\)</li></ul><div><br /></div><div>The solution idea is pretty straightforward. For any word \(w\), the minimum characters that have to be typed is equal to length of the shortest prefix \(p\) of \(w\) such that there is no word in the dictionary that has a prefix \(p\). It turns out that there is a very elegant solution using tries.</div><div><br /></div><h3>What is a trie?</h3><div>A trie is a data structure used to store string keys for fast look-up. There are many resources online that have explained tries better than I can. So, I am just gonna give an brief overview here and point you to useful online resources. Later, I will how tries can be implemented easily in a programming contest environment.</div><div><br /></div><div>Typically, a trie looks like this.</div><div class="separator" style="clear: both; text-align: center;"><a href="http://2.bp.blogspot.com/-mB9KnE31k1g/VOnvjrohVlI/AAAAAAAAAKI/mkPlSMFsmiQ/s1600/trie.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://2.bp.blogspot.com/-mB9KnE31k1g/VOnvjrohVlI/AAAAAAAAAKI/mkPlSMFsmiQ/s1600/trie.png" height="309" width="320" /></a></div><div><br /></div><div>Notice that every node contains 26 pointers corresponding to each letter in the English alphabet. The topmost node in the above figure is called the <b>root</b> node. We see that only the pointers corresponding to M, P and T are depicted. We can assume the other pointers to be NULL i.e. there are no words starting with characters other than M, P and T. When we follow the M pointer we reach another node which has pointers only for A and E. This means that there are words in the trie with prefixes MA and ME. If we continue following the E branch we reach a node with a Δ symbol. The Δ symbol signifies that a word ends there. This means that the word \(\text{MENDEL}\) is present in the trie. This node also has a pointer corresponding to E. This is <b>not</b> the beginning of a new word. It is just a continuation of \(\text{MENDEL}\). Following the pointers leads us to the word \(\text{MENDELEEV}\). Similarly, the words \(\text{MAXWELL}\), \(\text{PASTEUR}\), \(\text{PAVLOV}\), \(\text{PEANO}\), \(\text{POINCARE}\), \(\text{POISSON}\) and \(\text{TURING}\) are present in the trie.<br /><br />To know more about tries, follow these links: <br /><ul><li><a href="http://www.geeksforgeeks.org/trie-insert-and-search/" target="_blank">Trie - GeeksforGeeks</a></li><li><a href="http://en.wikipedia.org/wiki/Trie" target="_blank">Trie - Wikipedia</a></li><li><a href="http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=usingTries" target="_blank">Topcoder tutorial on tries</a></li></ul><br />If you have visited above links you would have noticed that tries have complicated and messy implementation involving dynamic memory allocation. This is unsuitable for programming contests. Here is a more contest-friendly trie implementation.<br /><pre class="prettyprint">int a[N][26];<br />char s[N];<br />int n = 1;<br /><br />scanf("%s", s);<br />int t = 1;<br />for (int i = 0; s[i]; ++i) {<br /> int p = s[i] – 'a'<br /> if (a[t][p] == 0) {<br /> n++;<br /> a[t][p] = n;<br /> }<br /> t = a[t][p];<br />}</pre>The above shows how to implement the insert operation in a simple way. All we are doing here is simulating dynamic memory allocation in place of actually using it. In the above code <code>a[][]</code> denotes the trie and <code>n</code><span style="font-family: inherit;"> denotes the index of the next available node. If you have read the GeeksforGeeks article it should not be very difficult to understand how the above code works. Can you figure out how to write code for the look-up operation? Here is my implementation:</span></div></div><pre class="prettyprint">bool isPresent(char *s) {<br /> int t = 1;<br /> for (int i = 0; s[i]; ++i) {<br /> int p = s[i] - 'a';<br /> if (a[t][p] == 0) return false;<br /> else t = a[t][p];<br /> }<br /> return true;<br />}<br /></pre>Know that we know how tries work and how to implement them, let's solve the original problem. As I pointed out earlier, we need to find, for every word, how much of the word has to be typed such that the rest of the word can be uniquely identified. This is the same as finding the shortest prefix of the word that is not present in the trie. Let's follow this algorithm:<br /><pre class="prettyprint">for every word w do:<br /> add it to the trie in the following way:<br /> during adding count the length of the longest prefix that is already present<br /> call this length <b>L</b><br /> add <b>L + 1</b> to the answer<br />repeat<br /></pre>This algorithm leads to a simple implementation. You don't even need to write a separate function for look-up. Here is my implementation:<br /><pre class="prettyprint">const int N = 1000050;<br /><br />int a[N][26];<br />char s[N];<br /><br />int main() {<br /> int t;<br /> scanf("%d", &t);<br /> long long ans;<br /> for (int tt = 1; tt <= t; ++tt) {<br /> printf("Case #%d: ", tt);<br /> int nn;<br /> scanf("%d", &nn);<br /> ans = 0;<br /> int n = 1;<br /> for (int i = 0; i < nn; ++i) {<br /> scanf("%s", s);<br /> int t = 1, ct = 0;<br /> bool end = false;<br /> for (int j = 0; s[j]; ++j) {<br /> int p = s[j] - 'a';<br /> if (!end) ct++;<br /> if (a[t][p] == 0) {<br /> n++;<br /> a[t][p] = n;<br /> end = true;<br /> }<br /> t = a[t][p];<br /> }<br /> ans += ct;<br /> }<br /> printf("%lld\n", ans);<br /> memset(a, 0, sizeof(a));<br /> }<br /> return 0;<br />}</pre>You can test your solution by submitting it in the <a href="http://codeforces.com/gym/100579/submit" target="_blank">Codeforces Gym</a> or by downloading the official input and output files. Check the Hacker Cup Facebook page for links.<br /><br />Thanks for reading!Unknownnoreply@blogger.com5tag:blogger.com,1999:blog-3018571607927897517.post-11858542704353038162015-02-14T22:07:00.000-08:002015-02-21T05:05:13.362-08:00Problem discussion: Facebook Hacker Cup 2015 Round 1 - HomeworkIt's been a long time since I last wrote on this blog and a lot has happened since then. The most important of all is that my personal website is up at <a href="http://anirudhrb.com/" target="_blank">anirudhrb.com</a> and consequently this blog can now be accessed at <a href="http://blog.anirudhrb.com/" target="_blank">blog.anirudhrb.com</a>. Also, you can now reach through email at <a href="mailto:mail@anirudhrb.com" target="_blank">mail@anirudhrb.com</a>.<br /><br />Let's get down to business now. The online part of Facebook Hacker Cup 2015 concluded recently and the top 25 contestants have been selected to compete in the onsite finals. The problem I am writing about in this post appeared in the Round 1 (there were 3 rounds).<br /><br />The title of the problem is "Homework" and here is the abridged statement:<br /><blockquote class="tr_bq">Given three integers \(A\), \(B\) and \(K\), find the number of integers in range \(\big[A, B\big] \) that have a "primacity" of \(K\). Primacity of a number \(N\) is defined as the number of distinct prime factors of \(N\). You have to process \(T\) such queries.</blockquote>The constraints are as follows:<br /><ul><li>\(2 \le A \le B \le 10^{7}\)</li><li>\(1 \le K \le 10^{9}\)</li><li>\(1 \le T \le 100\)</li></ul><div>The first thing to notice here is that the number of prime factors of \(10^{7}\) is not very large. In fact, it is at most \(8\). So, the maximum primacity of a number within the given constraints is \(8\). To know more about this read about <a href="http://en.wikipedia.org/wiki/Primorial" target="_blank">Primorial</a> on Wikipedia.</div><div><br /></div><div>We can modify the <a href="http://en.wikipedia.org/wiki/Sieve_of_Eratosthenes" target="_blank">sieve algorithm</a> to calculate the primacity of each number in the range \( \big[1, 10^{7}\big] \) in the following way: whenever you "cross-out" a number designating it as non-prime, increment the value at that position. This works because for any number \(x\), the primacity is equal to the number of primes less that \(x\) that divide it which is the same the number of times it is crossed out in the sieve algorithm.</div><div><br /></div><div>Now that we know the primacity every possible number within the constraints, we need to think about how to answer the queries efficiently. Notice that the answer can be expressed as follows</div><blockquote class="tr_bq">answer = answer for \(\big[1, B\big]\) \(-\) answer for \(\big[1, A - 1\big]\)</blockquote>We can answer queries in \(\mathcal{O}(1)\) time with some pre-computation. The pre-computation can be done this way<br /><pre class="pseudocode"><code>Let c[n][p] denote the number of integers in [1, n] with primacity p then,<br />c[n][p] = c[n - 1][p] + 1 if primacity of n is p<br />c[n][p] = c[n - 1][p] if primacity of n is not p</code></pre><div>We do this pre-processing for all possible values of primacity and answer each query in \(\mathcal{O}(1)\) time.<br /><br />You can test the correctness of your solutions by submitting it in the <a href="http://codeforces.com/gym/100579" target="_blank">Codeforces Gym</a> (login required) or you can download the official <a href="http://pastebin.com/tUftWCVR" target="_blank">input</a> and <a href="http://pastebin.com/60yhTXsR" target="_blank">output</a> files and test your solution against them. Here's my C++ code (shortened). You can find the <a href="https://raw.githubusercontent.com/anirudhrb/competitive-programming/master/Facebook%20Hacker%20Cup%202015%20Round%201/homework.cpp" target="_blank">full code on GitHub</a>.<br /><br /><pre class="prettyprint">const int N = 10000001;<br /><br />bool mark[N];<br />int p[N], ct[N][8];<br /><br />int main() {<br /> mark[1] = false;<br /> for (int i = 2; i < N; ++i) {<br /> if (mark[i]) continue;<br /> for (int j = 1; i * j < N; ++j) {<br /> mark[i * j] = true;<br /> p[i * j]++;<br /> }<br /> }<br /> for (int i = 2; i < N; ++i) {<br /> for (int j = 0; j < 8; ++j)<br /> ct[i][j] = ct[i - 1][j];<br /> ct[i][p[i] - 1]++;<br /> }<br /> int t, a, b, k;<br /> scanf("%d", &t);<br /> for (int tt = 1; tt <= t; ++tt) {<br /> scanf("%d %d %d", &a, &b, &k);<br /> printf("Case #%d: ", tt);<br /> if (k > 8) printf("0\n");<br /> else {<br /> int ans = ct[b][k - 1] - ct[a - 1][k - 1];<br /> printf("%d\n", ans);<br /> }<br /> }<br /> return 0;<br />}<br /></pre><br />I will be back next week with the next problem "Autocomplete" from the same round.<br /><br />Thanks for reading!</div>Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-3018571607927897517.post-84557407601840832572014-12-13T19:40:00.001-08:002014-12-13T19:45:39.937-08:00Problem discussion: ShufflingCardsDiv2 - Topcoder SRM 641 900pt<p>Topcoder SRM 641 happened a few days ago and featured an unusually easy problem set for division 2. Before continuing reading the post, please read the <a href="http://community.topcoder.com/stat?c=problem_statement&pm=13549&rd=16084&rm=324526&cr=23185389" target="_blank">problem statement</a> (login required).</p> <p>Now that you have read the problem statement, let me state the most important fact in the problem statement</p> <blockquote> <p><em>The cards are shuffled <strong>exactly</strong> twice.</em></p></blockquote> <p>This is very important because it makes the problem much easier. Since there are only two shuffles, there are a limited number of steps to be followed.</p> <p>Let’s analyze the shuffling procedure closely. In the beginning we have \(2N\) cards numbered from \(1\) to \(2N\). These cards are divided into two piles: one containing the first half cards \(1 \cdots N\) and the other containing the remaining cards. Let’s denote the cards which have number less than or equal to \(N\) as \(A\) and the others as \(B\). Now, the two piles will look like this</p> <blockquote> <p>\(\text{A A A A A …}\)<br>\(\text{B B B B B …}\)</p></blockquote> <p>After performing the interleaving, we will get a deck that looks like</p> <blockquote> <p>\(\text{A B A B A B A B A B . . .}\)</p></blockquote> <p>Now, we need to shuffle this deck again. Let’s assume that \(N\) is even. (We will cover the odd case later.) Divide the deck into two piles.</p> <blockquote> <p>\(\text{A B A B A B …}\)<br>\(\text{A B A B A B …}\)</p></blockquote> <p>The result after interleaving the above piles is given to us as input. Note that the two piles shown above are not exact i.e for any of the above piles we can take any permutation of the cards for interleaving. But, one condition still holds:</p> <blockquote> <p><em>In any pile, the number of \(A\)s is equal to the number of \(B\)s.</em></p></blockquote> <p>After interleaving, all cards in the top pile will appear at even indices (assuming 0-based indexing). So, we need to check all even indices of the given array/vector and count the number of \(A\)s and \(B\)s. If they are equal, then return <code>"Possible"</code> otherwise return <code>"Impossible"</code>. </p> <p>As I have said before, this solution is valid only if \(N\) is even. When \(N\) is odd the number of \(A\)s and \(B\)s need not be same. In fact, the number of \(A\)s will be 1 greater than the number of \(B\)s.</p> <p>Here’s the final code in C++:</p> <p><pre class="pseudocode"><strong>string</strong> shuffle (vector<<strong>int</strong>> permutation) {<br /> <strong>int</strong> countA, countB;<br /> <strong>int</strong> n = permutation.size();<br /> <strong>for</strong> (<strong>int</strong> i = 0; i < n; i += 2) {<br /> <strong>if</strong> (permuation[i] <= (n / 2))<br /> ++countA;<br /> <strong>else</strong><br /> ++countB;<br /> }<br /><br /> <strong>if</strong> ((n / 2) % 2) {<br /> <strong>if</strong> (countA - 1 == countB)<br /> <strong>return</strong> "Possible";<br /> <strong>else</strong><br /> <strong>return</strong> "Impossible";<br /> } <strong>else</strong> {<br /> <strong>if</strong> (countA == countB)<br /> <strong>return</strong> "Possible";<br /> <strong>else</strong><br /> <strong>return</strong> "Impossible";<br /> }<br />}<br /></pre> Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-3018571607927897517.post-1261173799362956272014-10-08T05:45:00.001-07:002014-10-08T06:47:13.549-07:00Problem discussion: Timus OJ 1225 Flags<div align="left">This was one of the interesting problems I solved last week. I was just checking out Timus OJ when I stumbled across it. Here is the abridged problem statement (read the <a href="http://acm.timus.ru/problem.aspx?space=1&num=1225" target="_blank">original statement</a> at Timus OJ):</div><blockquote><div align="justify"><em>Given \(n\), the number of stripes, find the number of distinct ways the stripes can be colored if the only colors that can be used are white, blue and red. These constraints should be satisfied:</em></div><ul><li><em>No two consecutive stripes should be of the same color</em> </li><li><em>A blue stripe must always be between a red and a white one or a white and a red one.</em></li></ul></blockquote>If you want to try to come up with a solution yourself first, now is a good time. I am going to explain my solution in the next paragraph. If you want to submit your solution you need to register at <a href="http://acm.timus.ru/">http://acm.timus.ru</a>.<br /><br /><div align="left">The first thing to notice is that the last stripe cannot be blue since a blue stripe can appear only between a red and a white stripe. So, the solution to the problem is \[\text{number of ways in which red is last + number of ways in which white is last}\] But how do we find those quantities? <br /><br /></div><div align="left">If the last stripe is red, the stripe before it has to be either white or blue. Suppose we have a number of arrangements in which the second stripe from the last is white. All these arrangements contribute to the number of arrangements with last stripe red because we can color the last stripe red in all such arrangements. But, this is not the only way we can get arrangements with the last stripe red because we haven’t yet considered the case in which the second to last stripe is blue.<br /><br /></div><div align="left">Now, suppose that the second to last stripe is blue. The stripe that comes before this can be either red or white. If that stripe is red then the last stripe cannot be red so we ignore that case. So, all arrangements in which the third stripe from the last is white can have the last stripe red if we add a blue and a red stripe (in that order). Observe that there are no more ways of getting the last stripe as red other than the two discussed (convince yourself of this).<br /><br /></div><div align="justify">Similar arguments can be made for finding the number of ways in which the last stripe is white. It is clear that we have managed to come up with a recursive solution to the problem. Let’s state the recurrence relation in a bit more formally.</div><blockquote><div class="math">Let \(f(i, c)\) be the number of ways of coloring the stripes \(1,\, 2,\, ...\, ,\,i\) such that the \(i^{th}\) stripe has the color \(c\).<br />\[ \begin{equation} f(i, c) = \begin{cases} f(i - 1, red) + f(i - 1, white) & \text{if } c = blue, \\ f(i - 1, white) + f(i - 2, white) & \text{if } c = red, \\ f(i - 1, red) + f(i - 2, red) & \text{if } c = white. \end{cases} \end{equation} \] So, the final answer will be \[ \begin{equation} answer = f(n, red) + f(n, white) \end{equation} \] where \(n\) is the total number of stripes. </div></blockquote>The next step is to observe that this recursive solution has overlapping sub-problems. If it is not obvious, you can convince yourself of this by drawing a recursion tree. So, we need to use <a href="http://en.wikipedia.org/wiki/Dynamic_programming" target="_blank">dynamic programming</a> to speed up our solution. We have \(3N\) dp states (why?) and hence the time complexity is \(\mathcal{O}(3N) = \mathcal{O}(N)\).<br /><br />As always I will leave the implementation to you. But, if you are stuck you can have a look at <a href="https://github.com/anirudhrb/competitive-programming/blob/master/Timus/Flags1225.java" target="_blank">my solution</a>.<br /><br />Have a nice day!Unknownnoreply@blogger.com2tag:blogger.com,1999:blog-3018571607927897517.post-37735308603776848772014-09-27T23:36:00.000-07:002015-02-14T03:38:26.917-08:00Introduction to Disjoint-set data structure through UVa 10583 – Ubiquitous Religions<div align="left">Its been quite a while since I last wrote and I feel terrible about it. So, I have decided that, starting from today, I will try to publish at least one post every week. Through these weekly posts, I will discuss the most interesting algorithmic problem I solved during the week. Some of them, like this one, will be accompanied with an introduction to a new data structure or algorithm I had to learn to solve the problem. Now, lets start disjoint-set data structure without further ado.<br /><br /></div><div align="left">Lets first analyze the problem and understand why we need disjoint-set data structure. Here is an abridged version of the problem statement (<a href="http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=359&page=show_problem&problem=1524" target="_blank">link to original problem statement</a>):</div><blockquote><div align="left"><em>There are n \((1 \leq n \leq 50000 )\) students in a university and for some given m \(( 0 \leq m \leq \frac{n(n - 1)}{2} )\) pairs of students you know that the students in each pair follow the same religion. Find the maximum number of religions that the students in the university believe in assuming that one student can follow exactly one religion.</em></div></blockquote><div align="justify">After reading the statement, we can make the following observations:</div><ul><li> <div align="justify">If <em>a</em> and <em>b</em> follow the same religion and <em>b</em> and <em>c</em> follow the same religion, then <em>a</em> and <em>c</em> follow the same religion </div></li><li> <div align="justify">All the <em>n</em> students of the university can be divided into different sets such that all students in the same set follow the same religion. Initially (i.e. before scanning the input), each student is in a different set. </div></li><li> <div align="justify">For a pair \( (a, b) \) in the input we need to find sets \(A\) and \(B\) such that \(a \in A\) and \(b \in B\) and combine them i.e. we have to find \( C = A \cup B\) and replace both \(A\) and \(B\) with \(C\). (Note that since a student cannot follow more than one religion, the sets \(A\) and \(B\) are either equal or disjoint.)</div></li></ul><div align="justify">So, we need a need a data structure that can, given \(a\) and \(b\), find sets \(A, B\) such that \(a \in A\) and \(b \in B\) and then find \(C = A \cup B\) efficiently. This is where disjoint-set data structure comes in.</div><blockquote><div align="justify"><em>A disjoint-set data structure, also called a union-find set or merge-find set, is a data structure that keeps track of a set of elements partitioned into a number of disjoint sets.</em></div></blockquote><div align="left">The most efficient implementation is using rooted trees and a couple of heuristic and that’s the only implementation I am going to discuss here although I urge you to explore the linked list implementation and understand why it is slow. Each set in the disjoint-set forest is represented by a tree in which each node points to its parent and the root of each tree is called the <em>representative</em> of the set. A representative of a set is an element of the set that is used as an identifier for the set i.e every set can be identified by its representative. This data structure should be able to perform these operations efficiently:</div><ul><li> <div align="justify"><code><strong>make-set(x)</strong></code> : creates a new set with \(x\) as its only member</div></li><li> <div align="justify"><code><strong>find(a)</strong></code> : finds the set that contains \(a\) i.e returns the representative of the set that contains \(a\)</div></li><li> <div align="justify"><code><strong>union(a, b)</strong></code> : merges (union) the sets containing <em>a</em> and <em>b</em> i.e it computes \(A \cup B\) where \(a \in B\) and \(b \in B\). This is achieved by finding the representative of \(a\) and \(b\) and then making one of them the parent of the other.</div></li></ul><div align="justify">Assuming that you explored the linked list implementation, you will notice that this is not faster than that in the worst case. To make it faster we need to use two heuristics: <em><strong>union by rank</strong></em> and <em><strong>path compression</strong></em>.<br /><br /></div><strong>Union by rank</strong> In the <code><b>union()</b></code> operation above there are no specific instructions on which set should be the parent. It is shown that optimal performance is achieved when the set with the higher <em>rank</em> is made the parent. <em>Rank</em> can be defined as an upper bound on the number of edges between in the longest path between the root and a leaf.<br /><br /><strong>Path compression</strong> During the <code><strong>find()</strong></code> operation, we travel from a given node to the root of the tree. During this traversal we can set the parent for each node in the path to be the representative element thereby reducing the height of the tree and making the subsequent <code><strong>find()</strong></code> operations faster. Note that the application of this heuristic does not change the rank because the rank is just an upper bound (which is useful for proving time complexity).<br /><br />It’s now time to implement this data structure. Here’s the pseudo code:<br /><pre class="code_block"> <em>Let parent[1 ... n] and rank[1 ... n] be arrays such that<br /> parent[x] stores the parent of node x and,<br /> rank[x] stores the rank of node x.</em><br /><br /> <strong><u>PROCEDURE</u></strong> make-set(x):<br /> 1. parent[x] := x<br /> 2. rank[x] := 0<br /><br /> <strong><u>PROCEDURE</u></strong> find(x):<br /> 1. <strong><u>if</u></strong> (parent[x] != x) <em>// path compression</em><br /> 2. parent[x] := find(parent[x])<br /> 3. <strong><u>return</u></strong> parent[x]<br /><br /> <strong><u>PROCEDURE</u></strong> union(x, y):<br /> 1. xRoot := find(x) <em>// find the set containing x</em><br /> 2. yRoot := find(y) <em>// find the set containing y</em><br /> 3. <strong><u>if</u></strong> (xRoot == yRoot)<br /> 4. <strong><u>return</u></strong><br /> 5.<br /> 6. <em>// use union by rank heuristic to merge</em><br /> 7. <strong><u>if</u></strong> (rank[xRoot] < rank[yRoot])<br /> 8. parent[xRoot] := yRoot<br /> 9. <strong><u>else</u></strong> <strong><u>if</u></strong> rank[xRoot] > rank[yRoot]<br />10. parent[yRoot] := xRoot<br />11. <strong><u>else</u></strong><br />12. parent[yRoot] := xRoot<br />13. rank[xRoot] := rank[xRoot] + 1</pre><br />It can be proved that the time complexity for union and find operations is \(\mathcal{O}(\alpha(n))\) where \(\alpha(n)\) is the very slowly growing inverse <a href="http://en.wikipedia.org/wiki/Ackermann_function" target="_blank">Ackermann function</a>. The value of \(\alpha(n)\) is less than 5 for all practical values of \(n\).<br /><br />Now that we know how the data structure works and how to implement it, solving the problem is straightforward. But, there’s a problem. The problem requires us to calculate the number of disjoint sets that have formed. To do this we just need to use a variable to keep track of the number of sets. Whenever <code><strong>make-set()</strong></code> is called, the variable has to be incremented by 1 and whenever <code><strong>union()</strong></code> is called the variable is decremented by 1 (Why?). Take it as a challenge to add this feature to your implementation. Here’s the algorithm (without the set counter) to solve the problem:<br /><pre class="code_block"><strong> <u>PROCEDURE</u></strong> Ubiquitous-Religions():<br /> 1. <em>// add each student to an individual set</em><br /> 2. <strong><u>for</u></strong> i = 1 to n:<br /> 3. make-set(i) <em>// add set {i} to the forest</em><br /> 4.<br /> 5. <em>// process each of the m pairs</em><br /> 6. <strong><u>for</u></strong> each pair (a, b):<br /> 7. union(a, b) // a & b follow the same religion<br /> 8.<br /> 9. <em>// the answer is the number of disjoint sets</em><br />10. answer := getNumberOfSets()<br />11. <br />12. <strong><u>return</u></strong> answer</pre><br /><div align="justify">As always, I will leave out the implementation specific details and encourage you to implement the data structure and the above algorithm yourself instead of copying someone’s code off the internet. If you don’t know where or how to start you can have a look at <a href="https://github.com/anirudhrb/competitive-programming/blob/master/UVa/UbiquitousReligions.java" target="_blank">my solution</a> to the problem (in Java). Also, if you want to submit your solution at UVa Online Judge be mindful of the input and output specifications.</div><br /><div align="justify">You can find <a href="http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=359" target="_blank">more problems related to disjoint sets</a> at UVa Online Judge.</div><br /><div align="justify">Have a nice day!</div>Unknownnoreply@blogger.com1tag:blogger.com,1999:blog-3018571607927897517.post-22245401993328531712014-04-02T08:51:00.000-07:002014-09-27T06:23:36.317-07:00DotA 2 offline with bots (without Steam)It is very strange and annoying that to play DotA 2 you have to log in to Steam and be connected to the internet. But this might not always be possible. In my case, I use my college internet and the ports required for Steam are blocked. So, I can’t use Steam (except for when I am at home). And since I can’t use Steam, I can’t download and play DotA 2 the usual way. So, in this post I am going to describe what I did to make DotA 2 start without Steam.<br/><br/>First of all, I asked my friend to give me a copy of his SteamApps folder. After I got it, I copied the folder into my computer and navigated to the DotA 2 directory and started <code>dota.exe</code>. After a few seconds the intro video (that bald guy) showed up followed by the loading screen. Then, suddenly, the loading stopped and a message appeared on the screen: “Steam missing or out of date”.<br/><br/>Now, to play offline with bots, the first thing you need to do is to cause the console to open whenever the game starts up. Once you get the console, you can use some commands to start an offline game. To cause the console to show at game startup you can execute <code>dota.exe</code> with the command line argument <code>-console</code>. You can do this through a command prompt or by creating a shortcut. In this article, I will use the command-line approach. Type this command into the Windows command prompt.<br/><br/><code>dota.exe -console -novid</code><br/><br/>The argument <code>-novid</code> is just stop the intro video from appearing. The loading screen will directly appear. Now, you should see the loading screen. After a few seconds, you will get a black screen with nothing but the console displayed. Now that you have the console, type the following commands one after the other.<br/><br/><code>sv_lan 1<br/>sv_cheats 1<br/>dota_start_ai_game 1<br/>dota_bot_set_difficulty 1<br/>map dota</code><br/><br/>After the last command the game will load and you will be taken to the all pick screen. Just pick your favorite hero and start playing!<br/><br/><strong><em>NOTE</em></strong>: This works even if you start Steam in offline mode. But, if you don’t have Steam, please make sure you are not connected to the internet before doing this. For some reason it doesn’t work if you are connected to the internet. Weird things start happening like: your hero not moving, all bots spawning on radiant side etc.Unknownnoreply@blogger.com2