<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-5915477284432606062</id><updated>2011-10-06T08:43:41.101-07:00</updated><title type='text'>Problem solving, programming, and research</title><subtitle type='html'>Tales from the deep recesses of academic research</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>54</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-6607573082463351927</id><published>2011-01-08T17:14:00.000-08:00</published><updated>2011-01-08T19:10:14.346-08:00</updated><title type='text'>Crash course on design - Stanford d.School</title><content type='html'>One of my colleagues from &lt;a href="http://www.workerexpress.com"&gt;WorkerExpress&lt;/a&gt;, Joe Mellin, invited me to join him for a design workshop being put on by the &lt;a href="http://dschool.stanford.edu/"&gt;Hasso Plattner Institute of Design at Stanford&lt;/a&gt;. Joe is a former graduate of the d.School's masters program. I'm interested in design and I even taught principles of user-centered design for a Human Computer Interaction course at the &lt;a href="http://www.uvic.ca"&gt;University of Victoria&lt;/a&gt;, so I was keen to join him.&lt;br /&gt;&lt;br /&gt;I really had no idea what the day would entail, I just knew to show up at 9am and I'd be done sometime around 1pm. Arriving at the workshop, I discovered there were to be over 100 people involved. We were instructed immediately that we'd be breaking up into three different groups. Once in those groups, we were told that we would be led through a full design cycle with a target demographic of potential users (see picture of design process below). Our goal was simply to &lt;span style="font-weight:bold;"&gt;design a new holiday experience for kids&lt;/span&gt;. The d.School had arranged to have approximately 80 students ranging from around 11 to 17 years of age to be our volunteer users. &lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rZy4UM398G0/TSkg4tnYX7I/AAAAAAAAAFg/KyAzI8lw8tI/s1600/photo.JPG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 239px;" src="http://2.bp.blogspot.com/_rZy4UM398G0/TSkg4tnYX7I/AAAAAAAAAFg/KyAzI8lw8tI/s320/photo.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5560011373655121842" /&gt;&lt;/a&gt;&lt;br /&gt;After this brief instruction, we were put into groups of four, where we quickly introduced ourselves, and then had to come up with interview questions to ask the kids that would soon be arriving to talk to us. Each group was also assigned a "d.leader", i.e. a student at the d.School, to help guide us through the process. &lt;br /&gt;&lt;br /&gt;One of the really interesting things about the d.School is that it brings together people from very diverse backgrounds to teach them how to become design experts. The workshop today was able to mimic that as each person in my group had very different backgrounds than myself. One was an MD now doing his MBA at Stanford, one was currently working in the medical school at Stanford, one works at Google, and finally there's me, a reformed academic now working at a start-up.&lt;br /&gt;&lt;br /&gt;We only had about 5 minutes to prepare for the interviews, then we interviewed two different pairs of students for about 15 minutes each. We asked them about their holidays, what were their likes/dislikes, did they miss school during the holidays, what was their favorite holiday ever, were their family around during the holidays, and so forth. &lt;br /&gt;&lt;br /&gt;Actually talking to users is &lt;span style="font-weight:bold;"&gt;always interesting and usually surprising&lt;/span&gt;. We had two very different groups of users. Our first group were two 12 year old boys. Their parents were generally quite busy with work over the holidays so family time was limited. They were basically left to their own devices. In contrast, our second group were two 12 year old girls that spent their holidays traveling all over the world with their families.&lt;br /&gt;&lt;br /&gt;Here's a picture of the notes we took during the interviews. Although difficult to tell, the sticky notes are organized into four different groups: feeling, quote, thought, and action.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rZy4UM398G0/TSkg-SqTwpI/AAAAAAAAAFo/Xy7tbEXEsGA/s1600/photo%25282%2529.JPG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 239px;" src="http://4.bp.blogspot.com/_rZy4UM398G0/TSkg-SqTwpI/AAAAAAAAAFo/Xy7tbEXEsGA/s320/photo%25282%2529.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5560011469498860178" /&gt;&lt;/a&gt; &lt;br /&gt;Following the interviews, all the groups came back together to be quickly introduced to the next part of the design process: defining our user, their needs, and the insight about the problem. Although the users from our interviews had very different holiday experiences, there were some common themes. For example, all of the students mentioned that they sometimes get bored during the holidays. They sometimes miss school because they get to see their friends at school, but not as regularly over the holiday. They also sometimes didn't seem to know what to do with their time following Christmas celebrations. &lt;br /&gt;&lt;br /&gt;Taking these themes into account, we determined that our users were &lt;span style="font-weight:bold;"&gt;extremely bright&lt;/span&gt;, &lt;span style="font-weight:bold;"&gt;junior high students&lt;/span&gt; that needed &lt;span style="font-weight:bold;"&gt;interaction&lt;/span&gt; and &lt;span style="font-weight:bold;"&gt;socialization&lt;/span&gt; in a world where they did not know how to choose &lt;span style="font-weight:bold;"&gt;what activities&lt;/span&gt; they do during the holidays (or something to that effect, can't remember exact phrasing).&lt;br /&gt;&lt;br /&gt;After our point of view was established, we brainstormed ideas for how to possibly address it (see picture below). We had crazy ideas like group travel for kids, city-wide laser tag, to more tangible solutions like a community center that organizes activities. We eventually settled on proposing an online application that was able to combine a lot of our ideas. The idea was to create a market place for kids to be able to express interest in participating in certain activities. Once enough kids expressed interest in a particular activity, all the leg work for actually making that activity happen would be handled by us, which could include transportation, equipment, some supervision, and so forth. Another way to think about it would be summer camp, but coordinated completely online and based on your personal interests.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rZy4UM398G0/TSkhEa4PJrI/AAAAAAAAAFw/Ja0C8wPuErI/s1600/photo%25283%2529.JPG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 239px;" src="http://4.bp.blogspot.com/_rZy4UM398G0/TSkhEa4PJrI/AAAAAAAAAFw/Ja0C8wPuErI/s320/photo%25283%2529.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5560011574783977138" /&gt;&lt;/a&gt;&lt;br /&gt;Using this idea as a basis, we designed a prototype and then interacted with different students to test the prototype. This time we had three high school girls as our users. These girls were pretty incredible, one even stated, "&lt;span style="font-style:italic;"&gt;this seems to add a level of complexity that's unnecessary&lt;/span&gt;". Only in Palo Alto, California would that statement be in a 17 year old females vocabulary!&lt;br /&gt;&lt;br /&gt;All in all, it was an extremely fun and interactive workshop that I highly recommend if you get the chance. I'd love to have all WorkerExpress engineers participate in this workshop as they join the company. It's a great way to learn the basics of user-centered design.&lt;br /&gt;&lt;br /&gt;Check out the video below. As an exercise to get us ready for brainstorming we played rock, paper, and scissors. Everyone starts as an individual competitor. When you lose, you become the winner's biggest fan. The video below is at the end of the day when there were only two left standing.&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/WWuMaO5qbws?hl=en&amp;fs=1"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/WWuMaO5qbws?hl=en&amp;fs=1" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/center&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-6607573082463351927?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/6607573082463351927/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2011/01/crash-course-on-design-stanford-dschool.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/6607573082463351927'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/6607573082463351927'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2011/01/crash-course-on-design-stanford-dschool.html' title='Crash course on design - Stanford d.School'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_rZy4UM398G0/TSkg4tnYX7I/AAAAAAAAAFg/KyAzI8lw8tI/s72-c/photo.JPG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-7292510448680218510</id><published>2011-01-03T10:08:00.001-08:00</published><updated>2011-01-03T10:26:15.848-08:00</updated><title type='text'>ACM ICPC Pacific Northwest Regional Competition</title><content type='html'>Although I was not heavily involved in the ACM ICPC competitions this past Fall, I followed the &lt;a href="http://www.acmicpc-pacnw.org/"&gt;Pacific Northwest&lt;/a&gt; regional quite closely. Several of my former students were competing for the &lt;a href="http://www.uvic.ca"&gt;University of Victoria&lt;/a&gt; team and of course my new university (Stanford) was also competing.&lt;br /&gt;&lt;br /&gt;UVic had a great start to the competition, but ran into issues in the second half and ended up finishing in 9th. However, given that the university has only recently started competing in the ACM ICPC competitions, a 4th place finish in 2009 followed by a 9th place finish in 2010 is a pretty fantastic result.&lt;br /&gt;&lt;br /&gt;The contest this year was quite interesting in a variety of ways. First, for the first time in a very long time the top team was not &lt;a href="http://www.stanford.edu"&gt;Stanford&lt;/a&gt; or the &lt;a href="http://www.ubc.ca"&gt;University of British Columbia&lt;/a&gt;. Second, although Stanford Red solved problem D at the 18 minute mark, there was 230 submissions for this problem, and only 3 were deemed correct. To anyone with much experience in these types of contests, this result looks a little fishy.&lt;br /&gt;&lt;br /&gt;Rather than recap all the events and outcome, I'll point you to Brad Bart's (SFU coach) recounting and interpretation of the events: &lt;a href="http://www.cs.sfu.ca/news/events/ACM/2010/"&gt;http://www.cs.sfu.ca/news/events/ACM/2010/&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I am personally not very happy with the way this screw up has been handled by the ACM ICPC. If you have an opinion, post it to the comments or better yet let ACM ICPC know what you think.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-7292510448680218510?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/7292510448680218510/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2011/01/acm-icpc-pacific-northwest-regional.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/7292510448680218510'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/7292510448680218510'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2011/01/acm-icpc-pacific-northwest-regional.html' title='ACM ICPC Pacific Northwest Regional Competition'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-2608954868498567744</id><published>2010-12-03T11:01:00.000-08:00</published><updated>2010-12-03T12:08:01.662-08:00</updated><title type='text'>Semantic Search - For Reals?</title><content type='html'>&lt;object width="450" height="270"&gt;&lt;param name="movie" value="http://www.youtube.com/v/Z5QsrJVE5Ys?fs=1&amp;amp;hl=en_US"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/Z5QsrJVE5Ys?fs=1&amp;amp;hl=en_US" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="450" height="270"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;Recently a project I am involved in at Stanford participated and won the &lt;a href="http://challenge.semanticweb.org/"&gt;Semantic Web Challenge&lt;/a&gt; at the &lt;a href="http://iswc2010.semanticweb.org/"&gt;International Semantic Web Conference&lt;/a&gt;. The Semantic Web Challenge is a competition for Semantic Web applications. For the uninformed, the Semantic Web &lt;a href="http://en.wikipedia.org/wiki/Semantic_Web"&gt;"is a group of methods and technologies to allow machines to understand the meaning - or 'semantics' - of information on the World Wide Web"&lt;/a&gt;. Essentially, the Semantic Web hopes to move web applications beyond simple syntax to actually supporting the "meaning" behind content.&lt;br /&gt;&lt;br /&gt;In the challenge, the focus is not so much on the technical or research aspects of the tools, but more on their usefulness for target users. The challenge helps demonstrate what semantic technologies can bring to society. &lt;br /&gt;&lt;br /&gt;Our application, &lt;span style="font-weight:bold;"&gt;&lt;a href="http://www.cs.vu.nl/~pmika/swc/submissions/swc2010_submission_4.pdf"&gt;NCBO Resource Index: Ontology - Based Search and Mining of Biomedical Resources&lt;/a&gt;&lt;/span&gt;, is a semantic search application for biomedical researchers (check out the video below). I designed and developed the user experience and interface. &lt;br /&gt;&lt;br /&gt;The application allows researchers in the biomedical sciences to perform "concept-based search" over 22 different biomedical resource databases. Thus, rather than a researcher typing in keywords, the researcher would type in concepts from their domain of interest, those concepts come from ontologies, the backbone of the Semantic Web. For the sake of simplicity, an ontology is a structured terminology that describes the terms within a specific domain, the properties of those terms and the relationships between them.&lt;br /&gt;&lt;br /&gt;In terms of user interaction and searching, the behavior is quite similar to conventional search engines like Google. The difference being that when you select a term from the auto-completion drop down, that term comes from an ontology that was used to index elements within the various biomedical resources. I also included helpful tag clouds (see figure below) for visualizing related concepts to your current search query and I use color intensity to represent more relevant resources based on your current search terms.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rZy4UM398G0/TPlMttvl1hI/AAAAAAAAAFU/nt6KRqa9lJs/s1600/screenshot.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 207px;" src="http://3.bp.blogspot.com/_rZy4UM398G0/TPlMttvl1hI/AAAAAAAAAFU/nt6KRqa9lJs/s320/screenshot.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5546548764340508178" /&gt;&lt;/a&gt;&lt;br /&gt;The power of the search tool comes from how the index gets constructed. The &lt;a href="http://www.bioontology.org/"&gt;National Center for Biomedical Ontology&lt;/a&gt; maintains a project called &lt;a href="http://bioportal.bioontology.org/"&gt;BioPortal&lt;/a&gt;. This project is an open library of more than 200 ontologies in biomedicine. Using the terms from these ontologies as our term-base, we automatically annotate or "tag" textual descriptions of the data residing within the elements of the 22 biomedical resources. These could be things like patient records, gene expression data, research articles, clinical trials, etc. &lt;br /&gt;&lt;br /&gt;These annotations act as a link connecting an ontology term to a data element. The really useful part of this process and also where the semantics begin to play a role, is that we can use the structure of the ontology to expand these annotations. So, rather than getting a simple keyword like "breast cancer" mapping to a particular clinical trial, we also know any synonyms of "breast cancer" described in the ontology. We can also use the hierarchy of the ontology to link more general or specific terms to the resource like "cancer" or "melanoma". Finally, we can use mappings between multiple ontologies to discover other related terms.&lt;br /&gt;&lt;br /&gt;This is truly where the power of ontologies can be seen and where a semantic approach to search can be very useful. For example, searching for the keywords "retroperitoneal neoplasm" within the &lt;a href="http://www.ncbi.nlm.nih.gov/geo/"&gt;Gene Expression Omnibus&lt;/a&gt; website will return zero results. However, the same search in our tool will retrieve relevant results annotated by the child term of "retroperitoneal neoplasm" from the NCI Thesaurus, "pheochromocytoma". Results are scored based on the distance of the matching annotation from a given search concept.&lt;br /&gt;&lt;br /&gt;Another big advantage to our application is that the ontologies form a "semantic bridge" between very different biomedical resources. One search execution automatically gives you access to 22 different databases, allowing a researcher to explore relationships between things like gene expressions and clinical trials relevant to a specific concept. Without this, researchers are forced to open up multiple web pages and search each database independently. &lt;br /&gt;&lt;br /&gt;Of course all these annotations take a long time to index and it results in a boat load of data. The current index is stored in a 1.5 terabyte MySQL database that contains 16.4 billion annotations, 2.4 million ontology terms, and 3.5 million data elements (stylized graphic below). Other members of the team have worked to figure out clever ways to do a lot of this indexing rather efficiently. You can read about it in Paea Lependu's paper &lt;a href="http://iswc2010.semanticweb.org/accepted-papers/146"&gt;Optimize First, Buy Later: Analyzing Metrics to Ramp-up Very Large Knowledge Bases&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rZy4UM398G0/TPlMAnhlPPI/AAAAAAAAAFM/f0ispKTe92w/s1600/screenshot.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 220px;" src="http://3.bp.blogspot.com/_rZy4UM398G0/TPlMAnhlPPI/AAAAAAAAAFM/f0ispKTe92w/s320/screenshot.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5546547989577022706" /&gt;&lt;/a&gt;&lt;br /&gt;We are working to include more resources in the index and also speed up the search support. If you're interested in playing with the application, check it out in the &lt;a href="http://bioportal.bioontology.org/resources"&gt;BioPortal integration&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-2608954868498567744?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/2608954868498567744/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2010/12/semantic-search-for-reals.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/2608954868498567744'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/2608954868498567744'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2010/12/semantic-search-for-reals.html' title='Semantic Search - For Reals?'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_rZy4UM398G0/TPlMttvl1hI/AAAAAAAAAFU/nt6KRqa9lJs/s72-c/screenshot.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-6282378617394892855</id><published>2010-11-18T10:04:00.000-08:00</published><updated>2010-11-18T10:38:44.946-08:00</updated><title type='text'>Dinner with Thumbtack and Leaving Stanford</title><content type='html'>Before I get into this post, I should first explain that I have decided to leave Stanford after January, 2011 for the wild and whacky world of industry. I will be joining &lt;a href="http://www.workerexpress.com"&gt;WorkerExpress&lt;/a&gt; as the Chief Technical Officer. &lt;br /&gt;&lt;br /&gt;Briefly, WorkerExpress is start-up in San Francisco co-founded by &lt;a href="http://www.linkedin.com/in/joemellin"&gt;Joe Mellin&lt;/a&gt; and &lt;a href="http://www.linkedin.com/in/pfuent"&gt;Pablo Fuentes&lt;/a&gt;. We offer a service for blue collar workers to both market themselves and provide them with work. Our customers are contractors that essentially "lease" employees from us. We help the contractors by automatically matching available workers to the specifications of a particular job. We also provide a certain level of quality control on the workers in terms of their skill assessment, reference checks, internal reviews, background checks, and so forth. We use a lot of SMS technology to provide flexible on-demand labor.&lt;br /&gt;&lt;br /&gt;Another San Francisco start-up called &lt;a href="http://www.thumbtack.com/"&gt;Thumbtack&lt;/a&gt; is somewhat playing in the same space as us. While we provide on-demand labor for contractors, essentially a business to business service, Thumbtack provides a market place for local services (i.e. business to consumer service). Think &lt;a href="http://www.craigslist.org"&gt;Craigslist&lt;/a&gt; but less 1995.&lt;br /&gt;&lt;br /&gt;Joe and Pablo know some of the brains behind Thumbtack and as a result we were invited to Thumbtack's "world headquarters" for dinner last night. The headquarters is in a three-story house in San Francisco. One floor is dedicated to development, another is a board room, and there's also a dining and kitchen floor. A few of the developers actually live in the house too (can't beat that commute). &lt;br /&gt;&lt;br /&gt;I actually really like the idea of using a house for an office building. The advantage is that you generally have a built-in full kitchen along with a shower and even rooms to crash in after a long hacking session. I also think the rent is probably less expensive than an office building. &lt;br /&gt;&lt;br /&gt;A potential disadvantage is that you basically may never leave your work. However, in a start-up environment, this is often the case and if I am going to be working crazy hours, I'd prefer to be able to prepare decent meals and catch a nap if necessary.&lt;br /&gt;&lt;br /&gt;So, back to the dinner. I had not met any of the Thumbtack crew prior to the dinner. Joe and Pablo convinced me to bring along my wife, Theresa, claiming that this would be a social event and not about business. Of course, you stick me in a room full of other geeks/programmers, it's pretty hard to keep the conversation in a realm where someone without a computer science degree can actually participate. &lt;br /&gt;&lt;br /&gt;Theresa had not suffered that kind of geek assault since she accompanied me to the 2003 ACM ICPC world programming finals. Things got even worse when I realized one of the Thumbtack developers had competed in the ACM contests during his undergrad. Luckily our intern and sales guy from WorkerExpress were there to chat about non-programming related topics.&lt;br /&gt;&lt;br /&gt;Despite my concern for Theresa's boredom, I had a great time at the dinner. The team at Thumbtack seem to have the same kind of hunger to make a successful business as we do at WorkerExpress. They understand the kind of work environment that is necessary to work long hours. &lt;br /&gt;&lt;br /&gt;I am extremely excited to be starting full-time with WorkerExpress. I plan to hire a couple of software developers early in 2011. So if you're interested or know someone that is, shoot me an e-mail or leave a comment.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-6282378617394892855?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/6282378617394892855/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2010/11/dinner-with-thumbtack-and-leaving.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/6282378617394892855'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/6282378617394892855'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2010/11/dinner-with-thumbtack-and-leaving.html' title='Dinner with Thumbtack and Leaving Stanford'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-6193476187977990269</id><published>2010-11-05T09:14:00.000-07:00</published><updated>2010-11-05T10:27:57.334-07:00</updated><title type='text'>Bad design or evil plot to prevent caffeine addiction?</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rZy4UM398G0/TNQ8kwxXhtI/AAAAAAAAAE0/yzJM0Ph_DSE/s1600/photo.JPG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 239px;" src="http://1.bp.blogspot.com/_rZy4UM398G0/TNQ8kwxXhtI/AAAAAAAAAE0/yzJM0Ph_DSE/s320/photo.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5536116444210104018" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Above is a picture of the coffee maker we have at the &lt;a href="http://bmir.stanford.edu/"&gt;Stanford Center for Biomedical Research&lt;/a&gt;. Can you figure out how to make a large coffee with light strength?&lt;br /&gt;&lt;br /&gt;For the first month or two that I was at Stanford I did not know how to re-configure the coffee setting on this machine. I took whatever was the last configuration, so sometimes I got an expresso, sometimes a small coffee, or perhaps a large one, all at varying strengths. It wasn't until I finally observed someone that actually knew how to use the machine that I figured out how to change the coffee configuration.&lt;br /&gt;&lt;br /&gt;So, the answer to my original question, "Can you figure out how to make a large cup of light coffee?" is that you need to press your finger against the lights at the top of the machine to adjust the coffee size and strength. Not only do you need to press down on the light, you need to do it for about a second. Thus, for a large coffee with light strength, you would first hold your finger against the large cup on the left-hand side, once that light switches, then hold your finger against the single coffee bean on the right, indicating light strength. (See picture below, I've circled the "buttons" you need to press.)&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rZy4UM398G0/TNQ8pTEI1-I/AAAAAAAAAE8/CSB91PhPllg/s1600/photo_annotate.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 239px;" src="http://4.bp.blogspot.com/_rZy4UM398G0/TNQ8pTEI1-I/AAAAAAAAAE8/CSB91PhPllg/s320/photo_annotate.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5536116522135115746" /&gt;&lt;/a&gt;&lt;br /&gt;Is this poor design or am I just an idiot? Or perhaps I should have just asked someone about how to use the coffee machine? Well, first, I couldn't really ask someone because A) I'm a dude and B) I have a PhD in Computer Science preventing me from ever acknowledging that I don't understand something technical :-). So that option is out.&lt;br /&gt;&lt;br /&gt;As for, am I just an idiot? My argument is of course, no, I am not :-). As further empirical evidence, I also know that I am not the only person that has struggled to use this thing. I was actually getting coffee recently and a guy that has been in the group for several years asked me how I was able to get two cups at once. Therefore, my argument is, it's a coffee machine for crying out loud, anyone should be able to use it. So, assuming that, what's wrong with the design?&lt;br /&gt;&lt;br /&gt;The big &lt;span style="font-size:20px"&gt;big&lt;/span&gt; &lt;span style="font-size:24px"&gt;big&lt;/span&gt; issue with using what looks to simply be indicator lights as buttons to re-configure the coffee is that there is really no obvious feedback that the lights are something you can press. Not only that, but you have to press them for a period of time, so just running your finger over them, does not change their state. To toss out a Human Computer Interaction term, these lights have no pressing "affordance".  &lt;br /&gt;&lt;br /&gt;An affordance is "&lt;a href="http://www.jnd.org/dn.mss/affordances_and.html"&gt;an actionable property between the world and an actor&lt;/a&gt;". That simply means that certain objects have physical properties that indicate specific actions. For example, a lever affords pulling or pushing, handles are for holding, wheels are for turning, etc. However, a light isn't really for pressing. On the coffee machine, the light is flat, there is no physical feedback to indicate that a pressing action or a change in the coffee configuration is about to occur when you place your finger on it. &lt;br /&gt;&lt;br /&gt;The sad thing is, this is very easy to address. If the coffee manufacturer simply made the lights beveled or slightly concave like the iPhone's button and had them actually press in when you pushed your finger against it, I think people would figure it out.&lt;br /&gt;&lt;br /&gt;Alas, that's enough harping on this issue. One of the problems with learning about design is that it's a tough skill to turn off. You'll constantly see poor design and be irritated by it. Welcome to my life of torture :-).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-6193476187977990269?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/6193476187977990269/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2010/11/bad-design-or-evil-plot-to-prevent.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/6193476187977990269'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/6193476187977990269'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2010/11/bad-design-or-evil-plot-to-prevent.html' title='Bad design or evil plot to prevent caffeine addiction?'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_rZy4UM398G0/TNQ8kwxXhtI/AAAAAAAAAE0/yzJM0Ph_DSE/s72-c/photo.JPG' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-3693326801820528283</id><published>2010-10-25T15:49:00.000-07:00</published><updated>2010-10-27T13:34:23.637-07:00</updated><title type='text'>Visualizing your social circle - mashing up GMail and Facebook</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rZy4UM398G0/TMh9nK_4jnI/AAAAAAAAAEU/MVPROZl1iNc/s1600/theresa_graph.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 181px;" src="http://3.bp.blogspot.com/_rZy4UM398G0/TMh9nK_4jnI/AAAAAAAAAEU/MVPROZl1iNc/s320/theresa_graph.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5532810254145326706" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;What's this? Another post? Yes indeed, two within a week of each other. My blog tends to work this way, my writing comes in waves. This post relates to data mining, visualization, and mashing up &lt;a href="http://www.gmail.com"&gt;GMail&lt;/a&gt; with &lt;a href="http://www.facebook.com"&gt;Facebook&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I attended a talk last week at the CS department at Stanford and the speaker mentioned that they were using the "TO" field in e-mails to perform hierarchical clustering to discover social groups. The intuition is pretty simple, you typically only include multiple people in an e-mail if they make up some kind of social group that you interact with. &lt;br /&gt;&lt;br /&gt;I thought I'd try out this idea on my own GMail account. After digging through the GMail's API documents, I downloaded a Java example of how to connect to your account using IMAP and OAuth. I wrote a Java program to go through all my Sent messages since I first created my GMail account back in 2005 and construct clusters based on the TO and CC fields. &lt;br /&gt;&lt;br /&gt;The algorithm is very basic. If an email was sent to more than one person, then I assume those people are somehow associated. So, assume I sent an email to people A and B. I would append person B to A's list of associations and append A to B's list of associations. I did this for 5 years worth of sent messages.&lt;br /&gt;&lt;br /&gt;The data I was getting looked pretty reasonable. I had clusters consisting of different people I've worked with over the past 5 years, people I socialize with, and students I've taught. &lt;br /&gt;&lt;br /&gt;There's a number of potential applications for this. One could be making suggestions for label groups in GMail. Another idea is to use this kind of mining to suggest groupings in something like Facebook. To experiment with that idea, I took the groups I mined in GMail and mashed it up with data publicly available on Facebook.&lt;br /&gt;&lt;br /&gt;That is, for each e-mail in the list I mined, I used Facebook's API to search to see if that person exists within the Facebook universe. If they did, I then processed all their associations, looking those people up on Facebook via the search API. That information forms an adjacency list, linking people's Facebook profiles based on how I have interacted with them through e-mail.&lt;br /&gt;&lt;br /&gt;For example, if I e-mailed people A, B, and C together and at another time, e-mailed C, D, and E, and assume all those people exist on Facebook, then I end up with a graph description like this:&lt;br /&gt;&lt;br /&gt;A: [B, C]&lt;br /&gt;B: [A, C]&lt;br /&gt;C: [A, B, D, E]&lt;br /&gt;D: [C, E]&lt;br /&gt;E: [C, D]&lt;br /&gt;&lt;br /&gt;So, that's kind of fun, but these graph structures would be a lot more interesting to look at if there was a way to visualize them. Well, I just so happen to be working on a JQuery graph visualization plugin that wraps a graphing library called &lt;a href="http://www.thechiselgroup.org/flexviz"&gt;FlexViz&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;After a little more hacking, I made a nice little web page for browsing these mashed up social groups. One such group is displayed at the top of this post. I created the group by extracting all people directly and indirectly associated with my wife (Theresa Demont). &lt;br /&gt;&lt;br /&gt;If you look at the picture below (same as at the top, but annotated), there's basically two groups here. One on the left and one on the right. The two groups are connected by a bridge between Nick and Colton. This is because the people on the right are all students I coached in programming competitions. Nick also helped out with coaching for a while, but Nick is also friends with Theresa, so he bridges my &lt;i&gt;programming competition world&lt;/i&gt; and the &lt;i&gt;social world&lt;/i&gt; I have with my wife.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rZy4UM398G0/TMh-N2UaLZI/AAAAAAAAAEc/Ki9dexZw0xM/s1600/theresa_graph_annotated.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 181px;" src="http://4.bp.blogspot.com/_rZy4UM398G0/TMh-N2UaLZI/AAAAAAAAAEc/Ki9dexZw0xM/s320/theresa_graph_annotated.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5532810918609169810" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Another interesting example is the graph structure defined by my CrossFit associations. Below, the graph on the left are the various CrossFit people I interacted with through e-mail before I moved to California, while the graph on the right is the CrossFit people I've interacted with since moving to California. It appears that I do not bulk e-mail nearly as many CrossFitters now. The truth is, most of my social interactions have moved to Facebook.&lt;br /&gt;&lt;br /&gt;&lt;center&gt;&lt;table width="80%"&gt;&lt;tr&gt;&lt;td&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rZy4UM398G0/TMiDJpih-II/AAAAAAAAAEk/wdwbfli3tHQ/s1600/crossfit_graph.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 200px; height: 96px;" src="http://2.bp.blogspot.com/_rZy4UM398G0/TMiDJpih-II/AAAAAAAAAEk/wdwbfli3tHQ/s200/crossfit_graph.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5532816344017401986" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;td&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rZy4UM398G0/TMiDOO67DnI/AAAAAAAAAEs/DazuWQ6TIk0/s1600/crossfit_new_graph.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 146px; height: 200px;" src="http://2.bp.blogspot.com/_rZy4UM398G0/TMiDOO67DnI/AAAAAAAAAEs/DazuWQ6TIk0/s200/crossfit_new_graph.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5532816422771297906" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;Most of this effort was just for fun to see what the resulting application would look like. However, it's also kind of interesting way to browse some of your own history that you record through e-mail. In my graph, it's easy to see the various changes in my work associations as I've changed companies and universities. I have a UNB group, UVic group, and Stanford group. It would be great to add in a timeline filter so I can hide people in the graph based on when I actually communicated with them. This could actually be animated so you could visualize the changing landscape of your social circle!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-3693326801820528283?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/3693326801820528283/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2010/10/visualizing-your-social-circle-mashing.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/3693326801820528283'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/3693326801820528283'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2010/10/visualizing-your-social-circle-mashing.html' title='Visualizing your social circle - mashing up GMail and Facebook'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_rZy4UM398G0/TMh9nK_4jnI/AAAAAAAAAEU/MVPROZl1iNc/s72-c/theresa_graph.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-5075850898628230841</id><published>2010-10-19T12:59:00.000-07:00</published><updated>2010-10-19T16:53:36.300-07:00</updated><title type='text'>Finally A Post! CrossFit Athlete Popularity?</title><content type='html'>It's been quite a while since I last posted. I have had lots of things to talk about, but just haven't found the time to write anything.&lt;br /&gt;&lt;br /&gt;In this post, I am going back to analyzing CrossFit-related data, but this time I am taking a completely different angle. Rather than analyzing scores and comparing athletes based on their performances, &lt;b&gt;I am comparing athletes based on their popularity and what the implications of popularity are within the CrossFit community&lt;/b&gt;.&lt;br /&gt;&lt;br /&gt;The first thing I had to consider with this project was how can athlete popularity be measured and secondly, where can I find lots of data to actually measure this value? I decided to go with a rather simplistic model. To measure popularity, I decided to base it on how often a particular individual is discussed in the CrossFit community.&lt;br /&gt;&lt;br /&gt;There's lots of potential data sources for this kind of information. I went with CrossFit.com. Any time an athlete is featured in an article, or has their results linked, or is discussed in the comments, I collected that information.&lt;br /&gt;&lt;br /&gt;The technical details regarding how I did this is pretty simple. I got a list of all 2010 Games athlete's names, then I wrote a small PHP script to make requests to every article posted on CrossFit.com over the past year. I then tracked the frequency of how many times I encountered a name within the article's text and comments. I tracked this information for all articles occurring before the CrossFit Games and for all articles after the Games. I also added in a few popular non-2010 competitors, like Pat Barber, Dave Lipson, Josh Everett, Pat Sherwood, and Kelly Starrett.&lt;br /&gt;&lt;br /&gt;There's some assumptions with this approach and as a result some data will be lost. There could be mentions that are not captured due to someone using a nickname or partial name or pronoun rather than the person's full name. Further, there's no image analysis, so I don't take the images from posts into account, which could potentially reference an athlete.&lt;br /&gt;&lt;br /&gt;The goal with dividing the data into two categories: &lt;b&gt;pre-games&lt;/b&gt; and &lt;b&gt;post-games&lt;/b&gt;, was so I can look at how the games influences someone's popularity. Does doing well in the games make you more popular? Why is that important?&lt;br /&gt;&lt;br /&gt;Using the two sets of frequencies, I normalized the values by the number of posts analyzed for each category (196 pre-games, 77 post-games). If you sort these normalized frequencies and plot them, one interesting thing is that they appear to follow a lognormal distribution. That is, very few athletes are mentioned a lot, and many athletes are mentioned occasionally. This is mostly interesting from a statistical perspective as many statistical tests assume the data is normal.&lt;br /&gt;&lt;br /&gt;Below is a table displaying the most highly mentioned athletes pre-games and a table displaying the most highly mentioned athletes post-games.&lt;br /&gt;&lt;br /&gt;&lt;table style="width:400px"&gt;&lt;tr&gt;&lt;td style="vertical-align:top"&gt;&lt;table border="1"&gt;&lt;caption&gt;Pre-Games Popularity&lt;/caption&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;Name&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;NF&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Rob Orlando&lt;/td&gt;&lt;td&gt;0.459&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Pat Sherwood&lt;/td&gt;&lt;td&gt;0.316&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Jason Khalipa&lt;/td&gt;&lt;td&gt;0.245&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Dave Lipson&lt;/td&gt;&lt;td&gt;0.204&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Chris Spealler&lt;/td&gt;&lt;td&gt;0.194&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Josh Everrett&lt;/td&gt;&lt;td&gt;0.158&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/td&gt;&lt;td style="vertical-align:top"&gt;&lt;table border="1"&gt;&lt;caption&gt;Post-Games Popularity&lt;/caption&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;Name&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;NF&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Pat Sherwood&lt;/td&gt;&lt;td&gt;0.714&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Graham Holmberg&lt;/td&gt;&lt;td&gt;0.675&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Rob Orlando&lt;/td&gt;&lt;td&gt;0.519&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Chris Spealler&lt;/td&gt;&lt;td&gt;0.519&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Dave Lipson&lt;/td&gt;&lt;td&gt;0.519&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Austin Malleolo&lt;/td&gt;&lt;td&gt;0.312&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;You can see that there's some fluctuation in the order, plus some athletes that were not in the original set of highly mentioned athletes are now in the list, in particular, Graham Holmberg and Austin Malleolo. This makes sense, as both Graham and Austin did very well this year (1st and 6th respectively), but Graham was not in the top in 2009 and Austin did not compete at all. However, there was a lot of anticipation and expectation with regards to former games competitors like Josh Everrett, Jason Khalipa, and Rob Orlando. Following the games, only Rob remained in the top for highly mentioned athletes. It's also interesting that Dave Lipson is in the top for both categories, even though he does not have a strong games history.&lt;br /&gt;&lt;br /&gt;To determine if there's a correlation between the measured athlete popularity and their finishing position in the games, I compared the athlete's post-games popularity scores with their games ranking. There was a statistically significant correlation between these two vectors. &lt;i&gt;Note, I had to remove any non-games athletes from this analysis.&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Following this, I compared the difference in popularity score pre and post-games for all athletes by subtracting their normalized frequency for the post-game category from the pre-game category. Athletes with large negative values indicate that their number of mentions increased greatly post-games, while athletes with large positive values mean the opposite.&lt;br /&gt;&lt;br /&gt;&lt;table border="1"&gt;&lt;caption&gt;Athlete Popularity Fluctuations&lt;/caption&gt;&lt;tr&gt;&lt;td&gt;&lt;b&gt;Rank&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;Name&lt;/b&gt;&lt;/td&gt;&lt;td&gt;&lt;b&gt;Difference&lt;/b&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;1.&lt;/td&gt;&lt;td&gt;Graham Holmberg&lt;/td&gt;&lt;td&gt;-0.614&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2.&lt;/td&gt;&lt;td&gt;Pat Sherwood&lt;/td&gt;&lt;td&gt;-0.398&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3.&lt;/td&gt;&lt;td&gt;Chris Spealler&lt;/td&gt;&lt;td&gt;-0.326&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;4.&lt;/td&gt;&lt;td&gt;Dave Lipson&lt;/td&gt;&lt;td&gt;-0.315&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;5.&lt;/td&gt;&lt;td&gt;Austin Malleolo&lt;/td&gt;&lt;td&gt;-0.291&lt;/td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td colspan="3" align="center"&gt;...&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;46.&lt;/td&gt;&lt;td&gt;Blair Morrison&lt;/td&gt;&lt;td&gt;0.026&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;47.&lt;/td&gt;&lt;td&gt;Josh Everett&lt;/td&gt;&lt;td&gt;0.041&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;48.&lt;/td&gt;&lt;td&gt;Tommy Hackenbruck&lt;/td&gt;&lt;td&gt;0.045&lt;/td&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;49.&lt;/td&gt;&lt;td&gt;Spencer Hendel&lt;/td&gt;&lt;td&gt;0.058&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;We can see from this table that Graham Holmberg's CrossFit.com presence increased dramatically after the games. His score is more than 4 standard deviations away from the mean. This is hugely significant. &lt;br /&gt;&lt;br /&gt;Pat Sherwood was consistently popular across both data sets, but he also had a huge increase in visibility post-games. I think a lot of this had to do with his success as a commentator during the games. &lt;br /&gt;&lt;br /&gt;&lt;i&gt;So why should anyone care about any of this?&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;A big reason, I think, is because as CrossFit becomes more mainstream and continues to grow, there's more money and business opportunities. There's starting to be huge financial incentives for doing well at the games or atleast becoming a popular athlete. The financial incentives go beyond the money that you win at the actual Games, but extend to your own business. Many of the games athletes are coaches and affiliate owners. Surely, being a "web-lebrity" is going to help drum up business. Furthermore, some athletes (i.e. Rob Orlando) are getting into selling equipment. Being a name within the community is going to help establish your brand and establish your company.&lt;br /&gt;&lt;br /&gt;Please don't think that I am insinuating that these athletes are competing solely for the money and business opportunities. But I think as the sport grows, visibility/popularity and establishing yourself as a "brand" is going to play a larger role. Similar to Olympic athletes, top-level CrossFitters may need sponsorship to allow themselves to have a full time training schedule. To entice sponsorship, even the most reluctant athletes may need to increase their presence in the community.&lt;br /&gt;&lt;br /&gt;&lt;i&gt;Why should you care if you will never be a Games competitor?&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;Well, I think from a business perspective, it's important to recognize how important this kind of visibility is. If you're a games competitor, you have a leg up on just about every other CrossFitter out there in terms of promoting your business. However, there are alternative means to increase your impact. &lt;a href="http://websmithblog.com/"&gt;Web Smith&lt;/a&gt; blogs about the importance of social mediums like Twitter and Facebook to help promote your business. &lt;br /&gt;&lt;br /&gt;In California, there are tons of CrossFit gyms. There are three just in Palo Alto. How do you separate yourself and how do you get potential members to choose you over so many alternatives? Well, you can potentially take your chances and train for the games or increase you popularity/presence through other means: Twitter, Facebook, sponsoring local athletes, competing locally, blogging, having a useful website, and possibly search optimization for your site.&lt;br /&gt;&lt;br /&gt;I am going to conclude this rather long post with my standard warning about this kind of data analysis. The intention is not to insult any athletes, but is merely a thought experiment for me. Also, there's many ways these results could be potentially analyzed. My interpretation and explanation may not be the whole story. Finally, I am making a rather large assumption here that popularity does lead to increased business. I do not know directly whether any of these athletes have had increased business at their boxes as a direct result of their web-presence.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-5075850898628230841?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/5075850898628230841/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2010/10/finally-post-crossfit-athlete.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/5075850898628230841'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/5075850898628230841'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2010/10/finally-post-crossfit-athlete.html' title='Finally A Post! CrossFit Athlete Popularity?'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-6861584499883022383</id><published>2010-06-13T09:48:00.000-07:00</published><updated>2010-06-14T10:31:06.579-07:00</updated><title type='text'>CrossFit Scoring - Alternative Perspective</title><content type='html'>This is my third foray into the world of CrossFit data analysis, this time looking at scoring. Recently, over at the &lt;a href="http://games2010.crossfit.com/"&gt;CrossFit Games website&lt;/a&gt;, there was an article about &lt;a href="http://games2010.crossfit.com/blog/2010/06/scoring-crossfit-competitions,543/"&gt;Scoring CrossFit Competitions&lt;/a&gt; as well as one talking about &lt;a href="http://games2010.crossfit.com/blog/2010/01/scorekeeping_technology,66/"&gt;Scoring Technology&lt;/a&gt;. I participated in a number of conversations in the comments section with regards to how competitions are scored. This post is going to be a bit of a summary of those conversations as well as a description of a new extension to the &lt;a href="http://keg.cs.uvic.ca/seanf/crossfit.php"&gt;CrossFit Data Explorer&lt;/a&gt; that allows people to explore different scoring schemes for any given competition (i.e. alternative perspectives).&lt;br /&gt;&lt;br /&gt;For those that are unaware, there are a variety of approaches to scoring competitions. I am not going to go into detail about these different metrics as they are described in both the articles I linked to above. The main thing to keep in mind is that each scoring system has certain "flaws" and depending on how an event is scored, there can be different outcomes.&lt;br /&gt;&lt;br /&gt;I wanted to explore some of these different scoring schemes as well as allow others to play with these different metrics. To do this, I added a new tab to the &lt;a href="http://keg.cs.uvic.ca/seanf/crossfit.php"&gt;CrossFit Data Explorer&lt;/a&gt; called "Scoring tables".  Check out the screenshot below displaying data from the &lt;a href="http://scores2010.crossfit.com/scoring/p/130/"&gt;Men's Northwest Regional&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rZy4UM398G0/TBZZqNWHEaI/AAAAAAAAADs/y7sOZ2Gd0js/s1600/northwest1.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 164px;" src="http://4.bp.blogspot.com/_rZy4UM398G0/TBZZqNWHEaI/AAAAAAAAADs/y7sOZ2Gd0js/s320/northwest1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5482668178041934242" /&gt;&lt;/a&gt;&lt;br /&gt;This view shows a table of the athletes, their results for each event, and it is sorted by the overall placement. On the left-hand side, you can choose how to score the athletes. The currently available scoring options are:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;Ranked-based&lt;/b&gt; - each athlete receives points based on their ranking in a workout, 1pt = 1st, 2pts = 2nd, ..., 50pts = 50th. The athlete with the lowest overall score wins.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Proportional&lt;/b&gt; - Athletes receive scores based on their relative performance to the top scoring athlete in an event. Highest overall score wins.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Lowest converted points (LCP)&lt;/b&gt; - Timed workouts receive 1 point per second, all other results are subtracted from this score. Lowest overall point total wins.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Standard score&lt;/b&gt; - Athlete receives points based on the distance between their score and the population mean. Highest score wins.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;The other feature supported by this new scoring component is that you can filter workouts from consideration in the ranking process. For example, if you thought, "Damn, if that stupid deadlift workout hadn't been in the regional, I would have made it." Well, now you can verify whether that's true :-).&lt;br /&gt;&lt;br /&gt;Ok, time to dig into comparing some scores. Keeping with the &lt;a href="http://scores2010.crossfit.com/scoring/p/130/"&gt;Men's Northwest Regional&lt;/a&gt;, if we re-rank the athletes based on the four supported scoring systems, we get the following results (top three athletes made games from this region). &lt;br /&gt;&lt;br /&gt;&lt;table border="1"&gt;&lt;tr&gt;&lt;th&gt;System&lt;/th&gt;&lt;th&gt;First&lt;/th&gt;&lt;th&gt;Second&lt;/th&gt;&lt;th&gt;Third&lt;/th&gt;&lt;tr&gt;&lt;td align="center"&gt;Rank-based&lt;/td&gt;&lt;td align="center"&gt;Chris Spealler&lt;/td&gt;&lt;td align="center"&gt;Jerome Perryman&lt;/td&gt;&lt;td align="center"&gt;Eric O'Connor&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align="center"&gt;Proportional&lt;/td&gt;&lt;td align="center"&gt;Chris Spealler&lt;/td&gt;&lt;td align="center"&gt;Jerome Perryman&lt;/td&gt;&lt;td align="center"&gt;Eric O'Connor&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align="center"&gt;LCP&lt;/td&gt;&lt;td align="center"&gt;Jerome Perryman&lt;/td&gt;&lt;td align="center"&gt;Jordan Holland&lt;/td&gt;&lt;td align="center"&gt;Christopher Dunkin&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td align="center"&gt;Standard&lt;/td&gt;&lt;td align="center"&gt;Chris Spealler&lt;/td&gt;&lt;td align="center"&gt;Jerome Perryman&lt;/td&gt;&lt;td align="center"&gt;Jordan Holland&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;As we can see, there's a certain amount of fluctuation in the results, especially for the LCP metric. In fact, with LCP, Eric O'Connor finishes 5th and Chris Spealler comes in 10th! (See screenshot below).&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rZy4UM398G0/TBZfqjC7NwI/AAAAAAAAAD0/7BruHFdbhxo/s1600/northwest2.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 165px;" src="http://2.bp.blogspot.com/_rZy4UM398G0/TBZfqjC7NwI/AAAAAAAAAD0/7BruHFdbhxo/s320/northwest2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5482674780936812290" /&gt;&lt;/a&gt;&lt;br /&gt;The major reason for this is the way the scoring system is designed. Each workout is scored completely independently from all other workouts, so a particular workout can completely dominate the final value. For example, coming in first in workout #1 at this regional gives you a score of 159 while coming in first in the second workout gives you a score of 10,908. That's a massive difference!&lt;br /&gt;&lt;br /&gt;In fact, if we look at the correlation between the LCP ranking and the ranking based purely on the second workout result, there's a strong correlation of 0.45 (p &lt; 0.01). In comparison to the actual official ranking results, the correlation between workout #2's ranking and the official ranking is only 0.37 (p &lt; 0.01).&lt;br /&gt;&lt;br /&gt;One really nice thing about the proportional, LCP, and standard score is that they "reward" people for being exceptional. For example, consider the &lt;a href="http://scores2010.crossfit.com/scoring/p/145/"&gt;Canada Men's Regional&lt;/a&gt; results. Erik Szakaly completed the Wall-ball/Pull-ups workout more than a minute ahead of the next fastest athlete. Check out the picture below, look at how far Erik's result is separated from all other athletes. Obviously this guy eats wall-balls for breakfast. &lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rZy4UM398G0/TBZjIpV2NJI/AAAAAAAAAD8/4G5eudL27uU/s1600/canada_wb.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 169px;" src="http://1.bp.blogspot.com/_rZy4UM398G0/TBZjIpV2NJI/AAAAAAAAAD8/4G5eudL27uU/s320/canada_wb.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5482678596557747346" /&gt;&lt;/a&gt;&lt;br /&gt;He received a low score of 1 point at the regional, but in the standard score system he would have received 2.17 points, almost a full point ahead of the next closest score (see below). This is a fairly significant margin in this scoring system.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rZy4UM398G0/TBZjsshkPWI/AAAAAAAAAEE/wF-wrLC_KDg/s1600/erik.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 163px;" src="http://3.bp.blogspot.com/_rZy4UM398G0/TBZjsshkPWI/AAAAAAAAAEE/wF-wrLC_KDg/s320/erik.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5482679215887498594" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Perhaps some kind of hybrid scoring system is needed. I think as CrossFit evolves, so will the scoring metrics, just as we see with other sports.&lt;br /&gt;&lt;br /&gt;As fun as it is to play with all this data, there's a few things to keep in mind. First is that switching the scoring system and re-ranking athletes assumes that the athletes would perform the same regardless of the scoring system. However, that's quite possibly not true. For example, Garth Prouse won the Canadian Regional run quite easily and received 1 point for coming in first (rank-based system). However, if he knew that he was being scored based on one of the other three systems, he may have pushed his pace harder to give himself a larger lead.&lt;br /&gt;&lt;br /&gt;Another assumption that is made is that the people that designed the workouts would include the same set of workouts regardless of the scoring system used. This may not be the case. For example, in the Northwest regional, perhaps the workout designers would have scored workout #2 differently if they were planning on using an LCP system so as to not bias the result in favor of performances in this workout.&lt;br /&gt;&lt;br /&gt;The other thing to be aware of is my application has to make certain assumptions. It assumes that all workouts with a time-based result are ranked lowest to highest, and all other workouts go highest to lowest. In the Canadian Regional, the run did not have times recorded, so including this event in the score comparison leads to misleading results.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-6861584499883022383?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/6861584499883022383/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2010/06/crossfit-alternative-perspective.html#comment-form' title='24 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/6861584499883022383'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/6861584499883022383'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2010/06/crossfit-alternative-perspective.html' title='CrossFit Scoring - Alternative Perspective'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_rZy4UM398G0/TBZZqNWHEaI/AAAAAAAAADs/y7sOZ2Gd0js/s72-c/northwest1.png' height='72' width='72'/><thr:total>24</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-5527081645404446644</id><published>2010-06-04T15:06:00.000-07:00</published><updated>2010-06-07T11:44:34.791-07:00</updated><title type='text'>More CrossFit Data Analysis - This time you can play too!</title><content type='html'>Don't care about my post and just want the good stuff, follow this link: &lt;a href="http://keg.cs.uvic.ca/seanf/crossfit.php"&gt;CrossFit Data Explorer&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;My last blog post was quite easily my most popular post to date (although my &lt;a href="http://seanfalconer.blogspot.com/2009/11/searchy-type-problems.html"&gt;Searchy-type problems&lt;/a&gt; post did receive a fair amount of attention). One former student of mine explained that my entry had, "the most practical findings for average people. Most of your posts even I have trouble understanding … you need a PhD".&lt;br /&gt;&lt;br /&gt;Ok, so my blog isn't always for the faint of heart :-). Ah well. However, this time I'm going mainstream again. Well, not quite mainstream, but as mainstream as CrossFit is :-).&lt;br /&gt;&lt;br /&gt;Due in part to the level of interest my last post generated as well as my own curiosity, I spent some more time looking at the CrossFit Games data. I've had some requests to analyze the women's data from the Canada Regional in the same way I analyzed the men's data. I haven't done that yet, but I think what I have done is pretty cool. And, more importantly, anyone can now interact and play with the data to discover their own interesting trends and results!&lt;br /&gt;&lt;br /&gt;I decided to build a visual tool for interacting with the various data available on the CrossFit Games website. I started out building it as a standalone Java application, but that seemed &lt;i&gt;soooo 1998&lt;/i&gt;. So, part way through development I abandoned the Java application in favor of a Web 2.0-style application, equipped with all the latest buzzword technologies (i.e. AJAX, jQuery, JSON, etc.).&lt;br /&gt;&lt;br /&gt;I've made the application available &lt;a href="http://keg.cs.uvic.ca/seanf/crossfit.php"&gt;online here&lt;/a&gt;, so feel free to play with it.&lt;br /&gt;&lt;br /&gt;There's three different visualizations available that allow you to compare athletes based on different event modalities as well as athlete rankings across various events. It should work with any URL that resolves to an overall results page (example page:  &lt;a href="http://scores2010.crossfit.com/scoring/p/145/"&gt;Men's Canadian Regional&lt;/a&gt;). I've pre-populated a drop down with all the regional results so you can select items from there or paste in an appropriate URL. The Men's Canadian Regional data is loaded by default. The application may not work with Internet Explorer, so I suggest Firefox, Safari or Chrome.&lt;br /&gt;&lt;br /&gt;In the &lt;b&gt;Compare events&lt;/b&gt; tab, you can set the graph axes to show results from different events. For example, using the default data, we can set the x-axis to be the first event, the 6.7 KM run, and set the y-axis to be the second event, the snatch complex. Comparing athlete values across these two events allows you to explore an athlete's strength versus endurance. If you look at the screenshot below, the tooltip shows that Garth Prouse was 1st in the run, but 30th in the snatch.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rZy4UM398G0/TAl5HLVZmMI/AAAAAAAAAC0/ZTRr5hncDyE/s1600/compare_events.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 320px; height: 187px;" src="http://1.bp.blogspot.com/_rZy4UM398G0/TAl5HLVZmMI/AAAAAAAAAC0/ZTRr5hncDyE/s320/compare_events.png" alt="" id="BLOGGER_PHOTO_ID_5479043585881512130" border="0"&gt;&lt;/a&gt;&lt;br /&gt;One interesting thing we can see in this view is how widely distributed the athletes are for various events. Consider the screenshot below where I plotted the overall placement versus the time for the double-under/burpee workout. Everyone is pretty closely clustered, but I've marked two distinct outliers. I'm guessing these two individuals must have struggled with double-under technique. Even those these two scores appear to be outliers, if you read my last post, there was little statistically significant difference between these athletes on this particular workout. In contrast, there is a lot of variance in the distribution for this workout when you inspect the women's data (second screenshot below).&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rZy4UM398G0/TAmtPLyjSwI/AAAAAAAAADc/nBSpvC2mzMI/s1600/du_burpee_compare.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 187px;" src="http://3.bp.blogspot.com/_rZy4UM398G0/TAmtPLyjSwI/AAAAAAAAADc/nBSpvC2mzMI/s320/du_burpee_compare.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5479100898047380226" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rZy4UM398G0/TAmv3vdRLoI/AAAAAAAAADk/2BoJleC81ro/s1600/women_du_burpee.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 186px;" src="http://3.bp.blogspot.com/_rZy4UM398G0/TAmv3vdRLoI/AAAAAAAAADk/2BoJleC81ro/s320/women_du_burpee.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5479103793839812226" /&gt;&lt;/a&gt;&lt;br /&gt;In the &lt;b&gt;Compare athlete rankings&lt;/b&gt; tab, you can inspect an athlete's ranking in each event as well as their overall ranking. For example, in the screenshot below I've selected only the top 6 athletes from the default data. We can see that three of these athletes (Erik, Nate, and Dan), for the most part, were pretty consistent across all events. On the other hand, DJ Wickham has an outlier on the run, while Garth and Michael have an outlier on the snatch complex event.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rZy4UM398G0/TAl59rh6nFI/AAAAAAAAAC8/jKLZWqwycrg/s1600/compare_athlete_rankings.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 320px; height: 189px;" src="http://4.bp.blogspot.com/_rZy4UM398G0/TAl59rh6nFI/AAAAAAAAAC8/jKLZWqwycrg/s320/compare_athlete_rankings.png" alt="" id="BLOGGER_PHOTO_ID_5479044522236877906" border="0"&gt;&lt;/a&gt;&lt;br /&gt;Finally, in the &lt;b&gt;Event rank comparison&lt;/b&gt; tab, you can compare an athlete's ranking in a specific event versus their overall placement. This allows you to visually correlate how closely tied an event's ranking for an athlete is in comparison to how they did after completing all events. The screenshot below shows all athletes and their rank in the run versus their overall ranking. We see that Cam's ranking went from 25th in the run to 34th overall, while Jason Fleming went from 10th to 50th and in the other direction, DJ Wickham went form 38th to 6th overall.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rZy4UM398G0/TAl6Cvc-hEI/AAAAAAAAADE/Krl-mezMtzo/s1600/event_rank_comparison.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 320px; height: 188px;" src="http://4.bp.blogspot.com/_rZy4UM398G0/TAl6Cvc-hEI/AAAAAAAAADE/Krl-mezMtzo/s320/event_rank_comparison.png" alt="" id="BLOGGER_PHOTO_ID_5479044609189250114" border="0"&gt;&lt;/a&gt;  &lt;br /&gt;For those with technical expertise or those just curious, the way the application works is I send the URL corresponding to the overall results for a sectional or regional competition to the server-side code. Taking whatever URL is provided, I make a server-side request to the URL, get the HTML contents and parse out the values of the overall results table using the &lt;a href="http://simplehtmldom.sourceforge.net/"&gt;PHP Simple HTML DOM Parser&lt;/a&gt;. I load this into a simple datastructure (a hashtable of hashtables), which describes the athletes, the events, and all the various results. This information gets encoded as JSON and sent back to the client (front-end).&lt;br /&gt;&lt;br /&gt;On the client, I convert the JSON text into a Javascript object/associative array. Then, based on whatever tab is selected, the data is processed into a data series for each athlete. I use &lt;a href="http://code.google.com/p/flot/"&gt;Flot&lt;/a&gt; to handle the rendering of the graphs. All the interactive behavior and some of the UI is built using &lt;a href="http://jquery.com/"&gt;jQuery&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The data is split into results for each event and overall placement. For each of these, the results are split into rankings and actual scores. For example, in the default dataset, athlete "Rogers, Dan", has both an overall rank of first as well as an overall placement score of 37. I do some simple things like recognize times, which for plotting purposes are converted into seconds.&lt;br /&gt;&lt;br /&gt;Please post to any questions, suggestions, or general comments. Please let me know if you discover anything interesting :-).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-5527081645404446644?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/5527081645404446644/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2010/06/more-crossfit-data-analysis-this-time.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/5527081645404446644'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/5527081645404446644'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2010/06/more-crossfit-data-analysis-this-time.html' title='More CrossFit Data Analysis - This time you can play too!'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_rZy4UM398G0/TAl5HLVZmMI/AAAAAAAAAC0/ZTRr5hncDyE/s72-c/compare_events.png' height='72' width='72'/><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-5168642502535937810</id><published>2010-06-01T10:17:00.000-07:00</published><updated>2010-06-01T11:20:16.675-07:00</updated><title type='text'>CrossFit Meets Data Analysis</title><content type='html'>It's been quite a while since I've updated. I've had lots to post about, but other things have taken priority. Today's update is going to be a little different, but I think it's pretty fun stuff.&lt;br /&gt;&lt;br /&gt;Any of my readers that know me personally know that besides being obsessed with computers and research related things, my other passion is fitness and sports. In particular, I'm really into something called &lt;a href="http://www.crossfit.com/"&gt;CrossFit&lt;/a&gt;. It would take an entire post to describe CrossFit in detail, so here's my very short explanation: it's the sport of exercise.&lt;br /&gt;&lt;br /&gt;One of the cool things about this growing "sport", is that there is a global competition called the &lt;a href="http://games2010.crossfit.com/"&gt;CrossFit Games&lt;/a&gt;. The games are kind of like the Olympics, but for CrossFit athletes. To qualify, you have to compete in two different levels of competition (sectional and regional), and place high enough to secure one of only 50 spots.&lt;br /&gt;&lt;br /&gt;This past weekend, I had several friends competing in the Canada-wide regional event. Only six men and six women from all of Canada get to go. Unfortunately, none of my friends made it (&lt;a href="http://scores2010.crossfit.com/scoring/canada/"&gt;full results&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;While observing the events, I got this idea about taking the results from the various events and doing some data analysis on it to see how the different athletes compare. I've been doing something similar in my regular research work, but for entirely different data. I decided to experiment with applying a similar clustering and statistical analysis approach, but to the CrossFit regional data!&lt;br /&gt;&lt;br /&gt;To start with, I wanted to try to classify athletes based on the similarity of their performances across the different events. Some athletes may be great runners, but may not be super strong. So there should be different classifications for athletes and each of these classes will have distinguishing characteristics.&lt;br /&gt;&lt;br /&gt;To do this, I downloaded the data from the CrossFit Games site and wrote a quick Java program to clean it up a bit. For my analysis, I considered each of the 5 events as different features describing a particular athlete. For example, my friend &lt;a href="http://runliftrow.blogspot.com/"&gt;Cam Birtwell&lt;/a&gt; would be described by the vector [25, 145, 793, 282, 776]. He placed 25th in the run (did not have times), lifted 145 lbs in the second event, and the last 3 numbers are his times in seconds to complete each event.&lt;br /&gt;&lt;br /&gt;Once I had vector descriptions for each athlete, I then used the &lt;a href="http://www.cs.waikato.ac.nz/ml/weka/"&gt;Weka&lt;/a&gt; data mining tool to perform K-Means Clustering on the data. K-Means tries to divide a given data set into K different clusters or classes. You have to choose K up front. Since I didn't know what a good K would be, I varied K from 2 up to 10, then used mathematical formulas to calculate the compactness and separation of the clusters I got. Based on these metrics, K=5, K=7, and K=8 appeared to be the best.&lt;br /&gt;&lt;br /&gt;After some exploration of the data, I decided K=7 seemed the most interesting and made the most sense. This means that out of the 50 or so male athletes competing in the Canada Regional, I split them up into 7 different groupings.&lt;br /&gt;&lt;br /&gt;Once I had this data, I wanted to know why a particular athlete, like Cam, was assigned to a particular cluster. To determine the distinguishing characteristics of each cluster, I performed multiple &lt;a href="http://en.wikipedia.org/wiki/Analysis_of_variance"&gt;Analysis of Variance&lt;/a&gt; (ANOVA) calculations to determine the statistically significant features of each cluster. That is, for each of the five features/event, I performed ANOVA to check for statistical difference in that particular feature when compared across all 7 clusters.&lt;br /&gt;&lt;br /&gt;For most events, there was statistically significant (p &lt; 0.05) differences. The event with the least difference was event 4, the double-under, burpee event (p = 0.03). This was such a fast event, it seems like every male athlete was quite competent at these skills, so there was little variance.  &lt;br /&gt;&lt;br /&gt;ANOVA simply told me that there was statistically significant differences between clusters, but not where these actually occur in the clustering. To determine this, I used the Tukey range test, which compares all pairs of means for a given independent variable (i.e. the event scores). Using these statistically interesting differences, I summarize the cluster characteristics below.&lt;br /&gt;&lt;br /&gt;&lt;table cellpadding="1" cellspacing="1" border="1"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th width="80"&gt;Cluster&lt;/th&gt;&lt;th&gt;Characteristics&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cluster 1&lt;/td&gt;&lt;td&gt;Fast runner, not a strong lifter, slow through event 3 and 4, and average in event 5.&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cluster 2&lt;/td&gt;&lt;td&gt;Fast runner, average lifter, fast through events 3, 4, and 5. All around athlete, with speed focus.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cluster 3&lt;/td&gt;&lt;td&gt;Average runner, heavy lifter, fast through events 3, 4, and 5. All around athlete, with strength focus.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cluster 4&lt;/td&gt;&lt;td&gt;Slow runner, weak lifter, slow through events 3, 4, and 5.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cluster 5&lt;/td&gt;&lt;td&gt;Average runner, low to medium lifter, slow in event 3, fast in event 4, and average in event 5.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cluster 6&lt;/td&gt;&lt;td&gt;Slow runner, medium lifter, slow in event 3, fast in event 4, and average in event 5.&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cluster 7&lt;/td&gt;&lt;td&gt;Slow runner, average to heavy lifter, average in event 3, fast in event 4, and average in event 5. Strong, but needs to work on running.&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;The following table gives a break down for each cluster, the highest placement for an athlete within that cluster, the lowest placement, the average and the size of the entire cluster.&lt;br /&gt;&lt;table cellpadding="1" cellspacing="1" border="1" width="90%"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;Cluster&lt;/th&gt;&lt;th&gt;Highest&lt;/th&gt;&lt;th&gt;Lowest&lt;/th&gt;&lt;th&gt;Average&lt;/th&gt;&lt;th&gt;Size&lt;/th&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cluster 1&lt;/td&gt;&lt;td&gt;24&lt;/td&gt;&lt;td&gt;45&lt;/td&gt;&lt;td&gt;35&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cluster 2&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;23&lt;/td&gt;&lt;td&gt;11&lt;/td&gt;&lt;td&gt;13&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cluster 3&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;15&lt;/td&gt;&lt;td&gt;10.83&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cluster 4&lt;/td&gt;&lt;td&gt;42&lt;/td&gt;&lt;td&gt;49&lt;/td&gt;&lt;td&gt;46.4&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;Cluster 5&lt;/td&gt;&lt;td&gt;13&lt;/td&gt;&lt;td&gt;43&lt;/td&gt;&lt;td&gt;28.6&lt;/td&gt;&lt;td&gt;9&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cluster 6&lt;/td&gt;&lt;td&gt;29&lt;/td&gt;&lt;td&gt;41&lt;/td&gt;&lt;td&gt;36.4&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Cluster 7&lt;/td&gt;&lt;td&gt;20&lt;/td&gt;&lt;td&gt;40&lt;/td&gt;&lt;td&gt;30.3&lt;/td&gt;&lt;td&gt;7&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;That's the raw data, but is there anything interesting we can say about this? It turns out there's a number of interesting results.&lt;br /&gt;&lt;br /&gt;First, the top 13 athletes all fall into cluster 2 or 3. Also, from the top 6 athletes (the ones going to the games), 5 of the 6 are from cluster 2, while one is from cluster 3 (DJ Wickham). Based on this, I'd guess that the 5 events favored an all around athlete with speed more so than an all around athlete with a strength focus. If you look at the events, most did not involve heavy weights. It could be that with a different balance in the events, for example, a 165 clean in jerk in the last event instead of 135, the cluster 3 athletes may have been more likely to dominate the top 6.&lt;br /&gt;&lt;br /&gt;One very obvious result from this is that you clearly need to be a great all around athlete to crack the top 15. With 5 events, your weaknesses are going to get exposed. Everyone in this competition is most likely a great CrossFitter, so it's extremely difficult to come back from even one poor score.&lt;br /&gt;&lt;br /&gt;I think it would be interesting to take this analysis further by looking at clustering data from other regionals and seeing the similarities between the athletes that made the games. It would also be interesting to use the analysis to compare the programming for the various regionals. What regions favor certain types of athletes?&lt;br /&gt;&lt;br /&gt;Anyway, that's all I got for now. Hopefully some people find this interesting and I hope no one that was included in the analysis is offended by my categorization. There's some subjectivity to the whole thing, so don't take it too seriously :-).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-5168642502535937810?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/5168642502535937810/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2010/06/crossfit-meets-data-analysis.html#comment-form' title='15 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/5168642502535937810'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/5168642502535937810'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2010/06/crossfit-meets-data-analysis.html' title='CrossFit Meets Data Analysis'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>15</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-3806767127705538252</id><published>2010-04-12T11:22:00.000-07:00</published><updated>2010-04-12T11:31:04.337-07:00</updated><title type='text'>OntoGraf Release</title><content type='html'>I have put up an &lt;a href="http://protegewiki.stanford.edu/index.php/OntoGraf"&gt;initial release&lt;/a&gt; on the OntoGraf visualization plugin for &lt;a href="http://protege.stanford.edu/"&gt;Protégé&lt;/a&gt; 4.1.&lt;br /&gt;&lt;br /&gt;Also, I created a demonstration video. There's a high resolution one available &lt;a href="http://www.stanford.edu/~sfalc/files/ontograf_demo.mov"&gt;here&lt;/a&gt; and I also uploaded a version to YouTube, which you can watch below. The YouTube one is a bit blurry unless you switch it to the 720 HD version.&lt;br /&gt;&lt;br /&gt;&lt;object width="440" height="385"&gt;&lt;param name="movie" value="http://www.youtube.com/v/JRNxvIZ5LBg&amp;hl=en_US&amp;fs=1&amp;"&gt;&lt;/param&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;/param&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/JRNxvIZ5LBg&amp;hl=en_US&amp;fs=1&amp;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="440" height="385"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-3806767127705538252?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/3806767127705538252/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2010/04/ontograf-release.html#comment-form' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/3806767127705538252'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/3806767127705538252'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2010/04/ontograf-release.html' title='OntoGraf Release'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-5131635168306790663</id><published>2010-04-06T09:17:00.001-07:00</published><updated>2010-04-06T10:02:08.349-07:00</updated><title type='text'>OntoGraf - Visualizing ontologies</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_rZy4UM398G0/S7tkK3tdVEI/AAAAAAAAACk/_cO2GsUpcFs/s1600/screenshot1.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 243px;" src="http://2.bp.blogspot.com/_rZy4UM398G0/S7tkK3tdVEI/AAAAAAAAACk/_cO2GsUpcFs/s400/screenshot1.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5457065511405573186" /&gt;&lt;/a&gt;&lt;br /&gt;This is my first post in a while. Unfortunately real life has been interfering with my blog updates of late. &lt;br /&gt;&lt;br /&gt;On the research side of things, my &lt;a href="http://www.stanford.edu/~sfalc/changeanalysis/changeanalysis.html"&gt;Change Analysis&lt;/a&gt; plugin has been released for a while and I've been making minor updates. I also started working on a web-based version, which I have running locally, but have not released yet.&lt;br /&gt;&lt;br /&gt;Recently I helped out at the &lt;a href="http://protege.stanford.edu/"&gt;Protégé&lt;/a&gt; short course. The short course is a three day course teaching the basics of ontologies, &lt;a href="http://www.w3.org/TR/owl-features/"&gt;OWL&lt;/a&gt;, and how to use Protégé to create/edit an ontology. &lt;br /&gt;&lt;br /&gt;The "students" learn about the currently three different flavors of Protégé. There's Protégé 3.4, which all of my ontology-related work has been integrated with. There is also &lt;a href="http://protegewiki.stanford.edu/index.php/WebProtege"&gt;Web Protégé&lt;/a&gt;, which was developed with &lt;a href="http://code.google.com/webtoolkit/"&gt;GWT&lt;/a&gt; and is a light-weight ontology editor. Finally, there's Protégé 4, which was a complete re-write of Protégé 3. &lt;br /&gt;&lt;br /&gt;I hadn't had much experience with Protégé 4 prior to the short course, so I actually learned a lot about its features and I am significantly impressed. It has a much cleaner interface and is a whole lot faster. Protégé 4 is built on top of &lt;a href="http://www.eclipse.org/equinox/"&gt;Equinox&lt;/a&gt;, the &lt;a href="http://www.eclipse.org"&gt;Eclipse&lt;/a&gt; implementation of the OSGi spec. The plugin architecture is very similar to developing plugins for Eclipse.&lt;br /&gt;&lt;br /&gt;As a developer, it takes a bit more work to get up and going with your Protégé 4 plugin versus Protégé 3, but I think the modularity that OSGi provides is worth the extra effort.&lt;br /&gt;&lt;br /&gt;The other major difference between the two versions of Protégé is that Protégé 4 supports the OWL 2 specification. It fully supports the &lt;a href="http://owlapi.sourceforge.net/"&gt;OWL API&lt;/a&gt;, and in my opinion there is a much clearer separation between the ontology-specific classes and the user interface.&lt;br /&gt;&lt;br /&gt;That being said, Protégé 4 is lacking in plugins in comparison to Protégé 3, especially for visualizing the structure of the ontology. As a result, I decided to port and improve an old plugin I wrote for Protégé 3 to this new version.&lt;br /&gt;&lt;br /&gt;The new plugin is called OntoGraf. The basic view shows the ontology tree on the left and a searchable interface to the right. The left and right panels are synchronized. The graph shown below is part of the Pizza example ontology. A radial layout has been applied and I have zoomed the graph out. When the graph is zoomed out, moving your mouse over a node will show that node at its normal size so you can read it (e.g. ThinAndCrispyBase below).&lt;br /&gt;&lt;br /&gt;The plugin hasn't been released yet, but I plan to release it soon. &lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rZy4UM398G0/S7tkS4KcLvI/AAAAAAAAACs/JwlCeQN6DyY/s1600/screenshot2.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 243px;" src="http://1.bp.blogspot.com/_rZy4UM398G0/S7tkS4KcLvI/AAAAAAAAACs/JwlCeQN6DyY/s400/screenshot2.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5457065648966086386" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-5131635168306790663?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/5131635168306790663/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2010/04/ontograf-visualizing-ontologies.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/5131635168306790663'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/5131635168306790663'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2010/04/ontograf-visualizing-ontologies.html' title='OntoGraf - Visualizing ontologies'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_rZy4UM398G0/S7tkK3tdVEI/AAAAAAAAACk/_cO2GsUpcFs/s72-c/screenshot1.PNG' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-8129245658136727079</id><published>2010-03-12T16:28:00.001-08:00</published><updated>2010-03-12T16:43:29.595-08:00</updated><title type='text'>Repainting a JTree</title><content type='html'>This is just going to be a quick little post about how to get around redraw issues when you need to repaint a JTree in Java. Sometimes when you force a repaint with a JTree, the nodes are rendered with a "..." at the end of the node text (see picture below). This happens when the text that needs to be rendered is outside the bounds of the Node object being painted. This really only happens when the text that needs to be rendered has changed between the last rendered state and the new repaint.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rZy4UM398G0/S5rdfEtSqHI/AAAAAAAAACc/LIyy2bN1TPQ/s1600-h/jtree_bad.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 200px; height: 166px;" src="http://3.bp.blogspot.com/_rZy4UM398G0/S5rdfEtSqHI/AAAAAAAAACc/LIyy2bN1TPQ/s200/jtree_bad.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5447910225167427698" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I've run into this issue a number of times due to all my various JTree hacking in projects like &lt;a href="http://www.stanford.edu/~sfalc/cogz/cogz.html"&gt;CogZ&lt;/a&gt;, &lt;a href="http://www.stanford.edu/~sfalc/zoomablejtrees/zoomablejtrees.html"&gt;ZoomableJTrees&lt;/a&gt;, and now again in my &lt;a href="http://www.stanford.edu/~sfalc/changeanalysis/changeanalysis.html"&gt;Change Analysis plugin&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Typically to force a JTree to repaint, you might do something like this:&lt;pre name="code" class="Cpp"&gt;myTree.invalidate();&lt;br /&gt;myTree.validate();&lt;br /&gt;myTree.repaint();&lt;br /&gt;&lt;/pre&gt;This code will force a re-rendering of the JTree object, but if the node sizes needed to be re-calculated, the tree will end up displaying as in my screenshot above. So, my trick to get around this issue is to actually change the JTree row size before I call the repaint, this will force the JTree to recalculate all the node size bounds.&lt;pre name="code" class="Cpp"&gt;// call setRowHeight to force the sizes to recalculate&lt;br /&gt;myTree.setRowHeight(DEFAULT_ROW_HEIGHT);&lt;br /&gt;// repaint this puppy&lt;br /&gt;myTree.repaint();&lt;br /&gt;&lt;/pre&gt;It's a bit weird, but it works :-).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-8129245658136727079?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/8129245658136727079/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2010/03/repainting-jtree.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/8129245658136727079'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/8129245658136727079'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2010/03/repainting-jtree.html' title='Repainting a JTree'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_rZy4UM398G0/S5rdfEtSqHI/AAAAAAAAACc/LIyy2bN1TPQ/s72-c/jtree_bad.png' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-3360703622727325376</id><published>2010-03-10T14:50:00.000-08:00</published><updated>2010-03-10T15:26:17.762-08:00</updated><title type='text'>Digging into ontology change sets</title><content type='html'>Lately I've been spending more time delving into some analysis of the changes being made to different ontologies by different editors and less time coding my &lt;a href="http://www.stanford.edu/~sfalc/changeanalysis/changeanalysis.html"&gt;change analysis plugin&lt;/a&gt;. One specific thing I have been investigating is how many times multiple editors actually modify the same artifact in an ontology and within that set, is there anything interesting going on with those potentially conflicting changes?&lt;br /&gt;&lt;br /&gt;For the programmers possibly reading this, you can think of the ontology as a software project. The artifacts of interest would be individual files. &lt;br /&gt;&lt;br /&gt;So far, in the three projects I've looked at, two of the three have very few multiple editors making changes to a single term. Unfortunately, I don't really have enough data to say anything conclusive yet about this observation. Once I have a longer history for a project, I'd like to see if is the general case. Also, I'd like to investigate what kind of roles people take on as editors. It would also be interesting to see what terms stabilize, with regard to their edits, over time, while I suspect others will perhaps be in constant evolution.&lt;br /&gt;&lt;br /&gt;During my initial analysis, I did come up with another feature for my plugin. While using the concept change analysis view that I talked about in my &lt;a href="http://seanfalconer.blogspot.com/2010/03/changeanalysistab-released.html"&gt;last post&lt;/a&gt;, I found sometimes I wished I could see the global impact that a single author or a set of authors had.&lt;br /&gt;&lt;br /&gt;To support this, I added a filter configuration dialog, which allows the user to filter the concept change tree to display only changes by certain authors or between certain date ranges. The date range feature will be very nice down the road when I have a longer history of changes. I'll be able to see where edits are taking place for specific time periods. I think, like software development, there will most likely be "hot spots" for changes over short periods of time.&lt;br /&gt;&lt;br /&gt;Below is an example of applying the author filter. The first image shows the filter dialog where I've chosen two authors to be filtered. Once I apply the filter, all changes by these authors will be hidden and the change counts in the tree will update. The two side by side images show the before and after screenshots of the concept change view.  As you can see when you compare the change counts in the tree between the two views, the two authors that are filtered are responsible for HUGE amount of changes in comparison to the others.&lt;br /&gt;&lt;center&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_rZy4UM398G0/S5gnMTgMA7I/AAAAAAAAAB8/lG0C8P07UJA/s1600-h/applying_filter.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 243px;" src="http://4.bp.blogspot.com/_rZy4UM398G0/S5gnMTgMA7I/AAAAAAAAAB8/lG0C8P07UJA/s320/applying_filter.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5447146841651807154" /&gt;&lt;/a&gt;&lt;/center&gt;&lt;table border="0" cellspacing="1" cellpadding="0" style="margin-top:-60px"&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;&lt;td&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rZy4UM398G0/S5gqALmeyyI/AAAAAAAAACM/1wJMrnGnLWk/s1600-h/concept_change_view.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 200px; height: 128px;" src="http://1.bp.blogspot.com/_rZy4UM398G0/S5gqALmeyyI/AAAAAAAAACM/1wJMrnGnLWk/s200/concept_change_view.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5447149931907173154" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/td&gt;&lt;br /&gt;&lt;td&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rZy4UM398G0/S5gqIUnVv3I/AAAAAAAAACU/SJckQFXOqhs/s1600-h/filtered_concept_change_view.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 200px; height: 128px;" src="http://1.bp.blogspot.com/_rZy4UM398G0/S5gqIUnVv3I/AAAAAAAAACU/SJckQFXOqhs/s200/filtered_concept_change_view.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5447150071765647218" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/table&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-3360703622727325376?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/3360703622727325376/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2010/03/digging-into-ontology-change-sets.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/3360703622727325376'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/3360703622727325376'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2010/03/digging-into-ontology-change-sets.html' title='Digging into ontology change sets'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_rZy4UM398G0/S5gnMTgMA7I/AAAAAAAAAB8/lG0C8P07UJA/s72-c/applying_filter.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-824542315776178311</id><published>2010-03-05T11:04:00.000-08:00</published><updated>2010-03-05T11:25:30.528-08:00</updated><title type='text'>ChangeAnalysisTab Released</title><content type='html'>The &lt;a href="http://protege.stanford.edu/"&gt;Protégé&lt;/a&gt; plugin that I have been working on, which I mentioned in my last few posts, has been released. It should be included in the Protégé build on Monday, but for now, people can download it from my &lt;a href="http://www.stanford.edu/~sfalc/changeanalysis/changeanalysis.html"&gt;website&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The original purpose of the plugin was really to help me with analyzing the changes being made to an ontology throughout the ontology's evolution. However, after showing some demos, people thought other Protégé users might be interested in also having access to this information.&lt;br /&gt;&lt;br /&gt;One of the views I like the most is the one shown in the screenshot below. On the left it shows the normal class hierarchy for an ontology, except that I've extended the tree to show how many changes exist within a branch and for a given term. The values in bold represent changes that have been made to that term, while the values in gray represent the number of changes that have been made to all subclasses within that branch.&lt;br /&gt;&lt;br /&gt;In the screenshot, you can see that there's been 2091 changes made within the "ICD Categories" branch (circled in red). However, what's interesting is that 73% of these changes or 1534 changes, have occurred all within the "Diseases of the skin and subcutaneous tissue" branch (also circled in red).&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_rZy4UM398G0/S5FX2Phj4LI/AAAAAAAAABs/-fP7NZYrO9s/s1600-h/concept_changes_example.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 215px;" src="http://3.bp.blogspot.com/_rZy4UM398G0/S5FX2Phj4LI/AAAAAAAAABs/-fP7NZYrO9s/s320/concept_changes_example.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5445230013859487922" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This is useful because it allows the managers of the project, or anyone that is interested, really see where the ontology's development is most active. In the future, I'd like to add in some controls for specifying a date range, that way you can see where the development is most active today, or say 6 months ago, or 2 years ago. I'm holding off a bit on this feature as all the sample projects I have with tracked changes are only starting their development so such fine grain control is unnecessary at the moment.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-824542315776178311?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/824542315776178311/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2010/03/changeanalysistab-released.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/824542315776178311'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/824542315776178311'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2010/03/changeanalysistab-released.html' title='ChangeAnalysisTab Released'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_rZy4UM398G0/S5FX2Phj4LI/AAAAAAAAABs/-fP7NZYrO9s/s72-c/concept_changes_example.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-994514709639374289</id><published>2010-03-01T10:38:00.000-08:00</published><updated>2010-03-01T11:18:28.511-08:00</updated><title type='text'>More fun with JFreeChart</title><content type='html'>As mentioned in my last post, I've been developing a new plugin for &lt;a href="http://protege.stanford.edu/"&gt;Protégé&lt;/a&gt; and I've been using the &lt;a href="http://www.jfree.org/jfreechart/"&gt;JFreeChart&lt;/a&gt; library for some of the visual components. Last time I discussed some issues with JFreeChart enforcing it's column and row keys to be Comparable, but then not actually using the compareTo method. I think I have figured out what the intention was behind this design.&lt;br /&gt;&lt;br /&gt;In some types of charts, the library will allow you to sort the domain axis, so I'm guessing it uses the Comparable for this functionality. Unfortunately, the DefaultCategoryDataset class that I am using to create my area charts does not support this, but I want my domain axis to be sorted by ascending date.&lt;br /&gt;&lt;br /&gt;I spent some time looking at the internal code of the library to try to figure out if there's a way I could do this. It turns out that the DefaultCategoryDataset uses a DefaultKeyedValues2D class to store it's actual data and this class supports a sort row keys option. However the DefaultCategoryDataset initiates the internal object with this always set to false. There's no public methods to access to the internal data object, so I can't modify it at runtime. &lt;br /&gt;&lt;br /&gt;Also, it doesn't appear that the code for sorting the key values really works as I would assume it should. Here's the internal code from the DefaultKeyedValues2D  class:&lt;pre name="code" class="Cpp"&gt;row = new DefaultKeyedValues();&lt;br /&gt;if (this.sortRowKeys) {&lt;br /&gt;   rowIndex = -rowIndex - 1;&lt;br /&gt;   this.rowKeys.add(rowIndex, rowKey);&lt;br /&gt;   this.rows.add(rowIndex, row);&lt;br /&gt;}&lt;/pre&gt;I would think that this sortRowKeys option should rely on the fact that the rowKey is Comparable, and should insert the new rowKey into the rowKeys list based on a comparison to the already existing keys. Instead all it does is insert the new rowKey at the beginning of the rowKeys list. This happens because of the line rowIndex = -rowIndex - 1. In this case, rowIndex will initially be equal to -1, the equation is -(-1) - 1, which is 1-1=0.&lt;br /&gt;&lt;br /&gt;So, even if I copied the internal code for DefaultCategoryDataset into my own class, and modified it's initiation of DefaultKeyedValues2D to set the sort key option to true, it still wouldn't actually sort the keys properly!&lt;br /&gt;&lt;br /&gt;In the end, I went with a somewhat hacky solution that I knew would work. While I process my data to display in the chart, I store all the unique dates that I encounter in a sorted set. Then, before I add all my values to the dataset, I make sure the first entry contains all the unique dates in the proper order. This forces JFreeChart's DefaultKeyedValues2D class to actually insert the row keys into its internal list in the proper order.&lt;br /&gt;&lt;br /&gt;Here's the hack:&lt;pre name="code" class="Cpp"&gt;// get the first entry in the list&lt;br /&gt;Entry&amp;lt;String, Map&amp;lt;Date, Integer&amp;gt;&amp;gt; firstEntry = &lt;br /&gt;    authorToChangesByMonthMap.entrySet().iterator().next();&lt;br /&gt;   &lt;br /&gt;// total hack to get the row keys to show up in sorted order&lt;br /&gt;for(Date date : allDates) {&lt;br /&gt;    // check to see if the key exists, if not, create it&lt;br /&gt;    if(firstEntry.getValue().get(date) == null) {&lt;br /&gt;      // dummy entry&lt;br /&gt;      firstEntry.getValue().put(date, 0);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;As for the plugin, I am planning to include it in the Protégé release that is scheduled for this week.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-994514709639374289?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/994514709639374289/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2010/03/more-fun-with-jfreechart.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/994514709639374289'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/994514709639374289'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2010/03/more-fun-with-jfreechart.html' title='More fun with JFreeChart'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-6256604575212027854</id><published>2010-02-24T10:59:00.000-08:00</published><updated>2010-02-24T11:59:11.046-08:00</updated><title type='text'>Blog changes and a real update</title><content type='html'>For anyone that actually reads this blog (yes, there is a handful), you may have noticed some changes. I decided to do a pseudo "re-branding" since I am now going to include posts about my latest academic work, and not just posts related to programming contests.&lt;br /&gt;&lt;br /&gt;As I mentioned in my last post, I've now been at Stanford for almost two weeks. One of the projects I am involved in is with the next revision of the &lt;a href="http://sites.google.com/site/icd11revision/"&gt;International Classification of Disease (ICD-11)&lt;/a&gt;. Researchers/developers at Stanford have been collaborating with the World Health Organization to develop software that will allow members of the medical research community to collaboratively develop ICD-11 in a somewhat open, crowd-sourcing manner.&lt;br /&gt;&lt;br /&gt;Members of Stanford's &lt;a href="http://protege.stanford.edu/"&gt;Protégé&lt;/a&gt; team have extended the web-based version of Protégé to help support this effort. For those unfamiliar with Protégé, it is a freely available ontology editor. If you don't know what an ontology is, for the purposes of this post, you can think of it as a taxonomy or hierarchy of terms.&lt;br /&gt;&lt;br /&gt;Now, one of the big issues with this project is managing this collaborative effort. There could potentially be all kinds of simultaneous editing, conflicts to resolve, and issues with simply understanding the evolution of the terminology. What I have started doing is analyzing the changes that have been made to the ontology so far.&lt;br /&gt;&lt;br /&gt;I have a bunch of researchy-type questions I want to answer through this analysis and at first, I thought I would just write some scripts to pull out the data. However, since this is so exploratory, I thought it might be more interesting and potentially useful to develop a tool that would allow me to explore the data. I started writing a Protégé plugin that provides different analysis views based on the tracked changes of a given ontology (in this case, the ICD-11 ontology).&lt;br /&gt;&lt;br /&gt;For one view in the plugin, I wanted to display changes made to terms over time. This way I could visually see how a term has evolved over a certain time period. I wanted this view to be similar to the &lt;a href="http://www.babynamewizard.com/voyager"&gt;NameVoyager&lt;/a&gt; applet. To create the area chart effect that I needed, I decided to use the &lt;a href="http://www.jfree.org/jfreechart/"&gt;JFreeChart&lt;/a&gt; library, which I have used a few times in past projects.&lt;br /&gt;&lt;br /&gt;Creating the view was reasonably simple, but I did run into one annoying technical detail that took a while to resolve. With JFreeChart, chart's use dataset objects to hold the data the chart is to represent. For my area charts, I used the DefaultCategoryDataset, which takes three parameters for adding new values; the actual numeric data value, a row key and a column key.&lt;br /&gt;&lt;br /&gt;For example, in my case, I want to show term changes over time, so my x-axis is going to be months, y-axis the number of changes, and I want to have a different data series or category for each term. Thus, in my DefaultCategoryDataset, the row key is the term name, while the column key is the month and year.&lt;br /&gt;&lt;br /&gt;The pesky technical issue was that I wanted my month and year to display like "July, 2009", but sorted along the x-axis based on a date comparison. The row and column keys can be any Comparable class, so I created my own DateKey class that implemented the Comparable interface. For displaying, JFreeChart simply calls the toString method on the row and column key objects, so in my DateKey class, I format the data object as I want in the toString method, but in my compareTo function, I call the Date class compareTo function.&lt;br /&gt;&lt;br /&gt;Based on the JFreeChart API, I assumed this would work, but I kept getting multiple entries for "September, 2009" for each term displayed in the visualization. I quickly realized that the compareTo function is never called. Eventually, after looking through the JFreeChart source code, I found that they store their row and column keys in a list. To determine if the key already exists, they call the list's indexOf method. This method does not use the compareTo function, it performs a linear O(n) search using the equals method.&lt;br /&gt;&lt;br /&gt;So, in order to make my DateKey work, I had to implement an equals method and hashCode function. JFreeChart's reliance on these methods rather than the compareTo function seems like a bug in their library. If compareTo is never called, then why must the row and column key's implement Comparable?&lt;br /&gt;&lt;br /&gt;Here's a couple of screenshots of what the view turned out to look like.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;table style="margin-top:-70px"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;br /&gt;&lt;a href="http://www.stanford.edu/~sfalc/images/change.png"&gt;&lt;img src="http://www.stanford.edu/~sfalc/images/change_small.png" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/td&gt;&lt;td&gt;&lt;br /&gt;&lt;a href="http://www.stanford.edu/~sfalc/images/change_heart.png"&gt;&lt;img src="http://www.stanford.edu/~sfalc/images/change_heart_small.png" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;Here's the final code for my DateKey class.&lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;public class DateKey implements Comparable&amp;lt;DateKey&amp;gt; {&lt;br /&gt; private static final DateFormat formatter = &lt;br /&gt;      new SimpleDateFormat("MMMMM, yyyy");&lt;br /&gt; private Date key;&lt;br /&gt; &lt;br /&gt; public DateKey(Date key) {&lt;br /&gt;  this.key = key;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; public Date getKey() {&lt;br /&gt;  return key;&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; public String toString() {&lt;br /&gt;  return formatter.format(key);&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; public boolean equals(Object o) {&lt;br /&gt;  return key.equals(((DateKey)o).getKey());&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; public int hashCode() {&lt;br /&gt;  return key.hashCode();&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public int compareTo(DateKey o) {&lt;br /&gt;  return key.compareTo(o.getKey());&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-6256604575212027854?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/6256604575212027854/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2010/02/blog-changes-and-real-update.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/6256604575212027854'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/6256604575212027854'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2010/02/blog-changes-and-real-update.html' title='Blog changes and a real update'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-3156497576028319118</id><published>2010-02-22T13:59:00.000-08:00</published><updated>2010-02-22T14:10:32.989-08:00</updated><title type='text'>So long UVic, thanks for the memories!</title><content type='html'>It's been a few weeks, but my time at the &lt;a href="http://www.uvic.ca"&gt;University of Victoria&lt;/a&gt; has come to a close. Last Fall I accepted a job at &lt;a href="http://www.stanford.edu"&gt;Stanford University&lt;/a&gt;, which started February 8th. I've been in California, working in the &lt;a href="http://bmir.stanford.edu/"&gt;Center for Biomedical Informatics Research Group&lt;/a&gt; for about a week and a half.&lt;br /&gt;&lt;br /&gt;I plan to stay somewhat involved with the UVic programming teams. John Hawthorn and Tyler Cadigan have taken over the weekly meetings as well as recruitment. I've been helping through e-mail whenever I'm needed. In the Fall I plan to help facilitate practice sessions between UVic, UBC, SFU, and Stanford as was done last year.&lt;br /&gt;&lt;br /&gt;I'm not sure what my plans are for my blog right now. I'm not sure whether I'll have that many programming team related updates now. I may start mixing in some discussion about the projects I am currently work on at Stanford. The coding I am doing here is more technical, engineering and research orientated, rather than specifically problem solving. However, it may still be of some interest.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-3156497576028319118?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/3156497576028319118/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2010/02/so-long-uvic-thanks-for-memories.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/3156497576028319118'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/3156497576028319118'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2010/02/so-long-uvic-thanks-for-memories.html' title='So long UVic, thanks for the memories!'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-8746341760187009485</id><published>2010-01-18T09:13:00.000-08:00</published><updated>2010-01-18T10:15:02.741-08:00</updated><title type='text'>BC Winter Programming Contest</title><content type='html'>The BC Winter Programming Contest took place this past weekend, January 16th. You can check out the &lt;a href="http://www.cs.sfu.ca/news/events/ACM/scoreboard/"&gt;scoreboard here&lt;/a&gt;. It was an excellent competition, only one student, Jaehyun Park from Stanford, solved all 7 problems, and almost all students solved at least one. &lt;a href="http://www.cs.sfu.ca/~bbart/"&gt;Brad Bart&lt;/a&gt; from SFU did most of the work to write problems, solutions, and organize everything. Sonny Chan (coach of Stanford) and myself made small contributions with some solution validation, test case generation and I wrote one of the problems.&lt;br /&gt;&lt;br /&gt;There were 5 pairs of UVic students competing and one UVicer competing remotely while on co-op with Research in Motion in Waterloo, ON. The top UVic team was Dan Sanders and John Hawthorn with 4 problems solved. They finished 6th overall and were the 2nd overall amongst BC students. They were in 3rd or 4th about half way through the competition, but got stuck trying to debug wrong answers on problems C, D and F for the second half of the competition. In the last half hour, students from SFU solved their 5th problem and jumped ahead of them. Scott Porter, competing remotely by himself, was the second top UVic student, placing 20th overall.&lt;br /&gt;&lt;br /&gt;Many of the UVic students that participated were completely new to competitive programming. They had really no idea what to expect, so I would like to congratulate all UVic students that came out and gave it a try. These competitions can be very intimidating and stressful the first few times, so I'm really proud of everyone that stuck it out for the full 3 hours. Hopefully you'll keep practicing and compete in the ACM competitions next Fall.&lt;br /&gt;&lt;br /&gt;Problems:&lt;br /&gt;A - This was the simplest problem in the set. You had to read in words and determine whether they were writable in Hawaiian. This amounted to determining whether the word contained only letters from the Hawaiian alphabet. The catch for some solutions was not properly handling the stopping condition. The problem states that the last test case ends with the exact string "Ahuihou". Some students matched against this word ignoring case, which is wrong.&lt;br /&gt;&lt;br /&gt;B - In this problem you have to read in a number, up to 30 digits in length, and determine what bases it can be rewritten in such that it is evenly divisible by 7. If the number is never divisible by 7, you have to print "Always unlucky.". It turns out that you only need to test bases up to base 16. All other valid bases will be one of these bases plus &lt;span style="font-style:italic;"&gt;Y&lt;/span&gt;*7 (&lt;span style="font-style:italic;"&gt;Y&lt;/span&gt; &gt;= 1). For example, if base 9 works, then base 16 works, 23, 30, etc. You can easily do this in Java using the BigInteger class. Python probably also has something equivalent.&lt;br /&gt;&lt;br /&gt;C - This was the hardest problem in the set, and only one student managed to successfully solve it. You are given a grid of diamond shaped cells. You are also given a metal detector that can probe a gridsquare and give you one of 8 compass directions. Compass directions North, East, South, and West are only valid responses if the treasure you are hunting is located along that exact path. Otherwise, you get a direction like Northwest that simply narrows your search to a region of the grid.&lt;br /&gt;&lt;br /&gt;You can solve this problem using dynamic programming or memoization. A sub-problem can be described by the grid size, &lt;span style="font-style:italic;"&gt;m&lt;/span&gt; and &lt;span style="font-style:italic;"&gt;n&lt;/span&gt;. However, there are some tricky conditions for the recursion for certain values of &lt;span style="font-style:italic;"&gt;m&lt;/span&gt; and &lt;span style="font-style:italic;"&gt;n&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;D - This is the problem I wrote. The problem describes a game with colored marbles on two tracks, one horizontal and one vaertical. You can rotate either track to move marbles from one track to the other. You are given an initial configuration of the tracks and you have to figure out the minimum number of moves (i.e. rotations)  such that you get all the blue marbles into the middle square. It turns out that there's not that many valid starting configurations. &lt;br /&gt;&lt;br /&gt;One approach to solve the problem is to start by representing the winning condition as two bit strings of length 18, "111100000111100000" and "100000111100000111". Then using these strings as a representation of state, you can breadth-first-search across all possible valid moves to generate any potential starting position that takes one move to reach the winning state. Continue to do this for all of these states to get all configurations 2 moves away. Do this until you can't find any new states and store everything in a table. You can now simply look-up the answer based on the read in configuration. If the given starting configuration does not exist in the table, then it is impossible.&lt;br /&gt;&lt;br /&gt;E - In this problem, you are told that two flat oranges rest at the bottom of a fruit bowl shaped like a parabola. You have to determine at what value of &lt;span style="font-style:italic;"&gt;y&lt;/span&gt; the fruit's centers are located at. Applying some geometry, you can work out an equation to calculate &lt;span style="font-style:italic;"&gt;y&lt;/span&gt; given some "guessed" value for &lt;span style="font-style:italic;"&gt;x&lt;/span&gt;. To guess a value for &lt;span style="font-style:italic;"&gt;x&lt;/span&gt;, we can use binary search and terminate when the high and low value become significantly close.&lt;br /&gt;&lt;br /&gt;F - This problem describes a bad measuring device that is only precise within 1 foot. So any measurement has value &lt;span style="font-style:italic;"&gt;V&lt;/span&gt; feet plus or minus 1 foot. The distance between floors in a building are measured with this device and you have to determine whether these measurements are consistent or inconsistent. Consistent means that the measurements are possible, inconsistent means that there's no way they could be right, there is some sort of conflict between two or more floor distances.&lt;br /&gt;&lt;br /&gt;We can represent this problem as a graph. All floors represent nodes in a graph and two floors, &lt;span style="font-style:italic;"&gt;a&lt;/span&gt; and &lt;span style="font-style:italic;"&gt;b&lt;/span&gt;, have directed edges between them if there is some measurement &lt;span style="font-style:italic;"&gt;d&lt;/span&gt; given in the problem input. The edge from &lt;span style="font-style:italic;"&gt;a&lt;/span&gt; to &lt;span style="font-style:italic;"&gt;b&lt;/span&gt; can be assigned weight 1-&lt;span style="font-style:italic;"&gt;d&lt;/span&gt; (the minimum possible value) and &lt;span style="font-style:italic;"&gt;b&lt;/span&gt; to &lt;span style="font-style:italic;"&gt;a&lt;/span&gt; has weight 1+&lt;span style="font-style:italic;"&gt;d&lt;/span&gt; (the maximum possible value). Once the graph is constructed, the measurements are consistent provided there are no negative cycles in the graph. To detect negative cycles, you can apply the &lt;a href="http://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm"&gt;Bellman-Ford algorithm&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;G - This was the second easiest problem in the set. You are given numbers that represent the values of odd-indexed nodes in a full binary tree. You have to figure out the values to the even-indexed nodes. You can represent the tree using a &lt;a href="http://en.wikipedia.org/wiki/Binary_tree"&gt;one-dimensional array&lt;/a&gt;. Most people learn this in undergraduate data structures courses. Then it's just a matter of filling in the array, where you use the odd-indexed values to calculate the even-indexed values. Instead of an array representation, you could also implement your own binary tree with left and right nodes, and apply recursion to fill in the tree.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-8746341760187009485?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/8746341760187009485/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2010/01/bc-winter-programming-contest.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/8746341760187009485'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/8746341760187009485'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2010/01/bc-winter-programming-contest.html' title='BC Winter Programming Contest'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-6157103940177859994</id><published>2010-01-14T10:09:00.000-08:00</published><updated>2010-01-14T10:56:05.250-08:00</updated><title type='text'>BC Winter Programming Contest Practice</title><content type='html'>The BC Winter Programming Contest is coming up this Saturday, January 16th. I've been making the rounds to different classrooms, trying to encourage some new students to give it a try. Last night I held a three hour practice session and luckily a few new faces showed up to give it a go. I picked five problems from the &lt;a href="http://acmgnyr.org/year2009/problems.shtml"&gt;2009 Greater New York Regional&lt;/a&gt; programming contest. I picked this set as I knew a lot of teams solved all the problems, so I figured it would have some pretty manageable problems for complete newbies.&lt;br /&gt;&lt;br /&gt;The students worked on problems A, C, D, H, and I. Most students solved A and D, and John Hawthorn was the only one to solve C.&lt;br /&gt;&lt;br /&gt;Problem A is quite straight-forward. You need to read in a list of 10 numbers and print the 3rd largest value. Probably the fastest way to code this is to read in the 10 numbers, sort them and print the 7th number in the array. This should only take a few lines.&lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;int P;&lt;br /&gt;cin &amp;gt;&amp;gt; P;&lt;br /&gt;while(P--) {&lt;br /&gt;  int num;&lt;br /&gt;  cin &amp;gt;&amp;gt; num;&lt;br /&gt;  vector&amp;lt;int&amp;gt; t(10);&lt;br /&gt;  for(int i = 0; i &amp;lt; 10; i++) cin &amp;gt;&amp;gt; t[i];&lt;br /&gt;  sort(t.begin(), t.end());&lt;br /&gt;  cout &amp;lt;&amp;lt; num &amp;lt;&amp;lt; " " &amp;lt;&amp;lt; t[7] &amp;lt;&amp;lt; endl;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;The second easiest problem was problem D. In this problem you have to read in a list of numbers and for every odd-indexed number, print the current median of the list. The key here is to make sure your sort is fast enough to get under the time limit and to follow their output specification exactly. Most students made mistakes with the output format. The built in sort routines in C++ or Java should be fast enough for this problem. &lt;br /&gt;&lt;br /&gt;Problem C is a little tricky to understand. The problem describes a scenario where you drop glass balls from different floors. The goal of dropping these balls is to determine what the last floor is that you could drop a ball from without having it break. In the classic version of this problem, sometimes involving eggs, you have to figure out the optimum strategy for this, which is a variation on binary search. However, in this problem they want you to figure out what the minimum number of drops is in the worst case. This means, if we choose the worst strategy, what is the best we can hope for?&lt;br /&gt;&lt;br /&gt;To solve this, we can try to enumerate all possible scenarios given the number of balls we have and the number of floors we can potentially drop a ball from. We can enumerate these states through recursion over two variables, our current number of balls left and the number of floors left.  We also need to use memoization to store our states so that we don't repeat states that we've already computed. Finally, we need to determine what the conditions are for this recursion. There is basically two cases, we drop a ball b from a floor f and the ball breaks leaving us with one less ball and knowing that we need to search floors below f, or the ball does not break, meaning we need to search floors above f. From these two scenarios, we want to keep track of the worst result, but the minimum overall value.&lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;int rec(int balls, int floors) {&lt;br /&gt;  if(balls == 1) return floors;&lt;br /&gt;  if(floors == 0) return 0;&lt;br /&gt;  if(floors == 1) return 1;&lt;br /&gt;  &lt;br /&gt;  // we already calculated this one&lt;br /&gt;  if(memo[balls][floors] &amp;gt;= 0) return memo[balls][floors];&lt;br /&gt;&lt;br /&gt;  int best = floors;&lt;br /&gt;  for(int f = 1; f &amp;lt;= floors; f++) {&lt;br /&gt;    // ball breaks on this floor, one less ball&lt;br /&gt;    int r1 = rec(balls-1, f-1);&lt;br /&gt;    // ball does not break, search above&lt;br /&gt;    int r2 = rec(balls, floors-f);&lt;br /&gt;    // keep track of the worst result&lt;br /&gt;    int result = 1 + max(r1, r2);&lt;br /&gt;    // keep track of the minimum for all worst cases&lt;br /&gt;    best = min(best, result);&lt;br /&gt;  }&lt;br /&gt;  memo[balls][floors] = best;&lt;br /&gt;  return best;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Problem H states that you need to find all lattice points contained within a convex polygon. All coordinates for the lattice points and the polygon vertices are integers. There's several ways you could solve this problem. One way is to do point in polygon tests to determine which lattice points are contained within the polygon. To do this, you can calculate all the potential lattice points by determining the maximum x-coordinate, maximum y-coordinate, minimum x-coordinate and minimum y-coordinate based on all vertices in the polygon. Then, you can loop from the minimum y to maximum y, minimum x to maximum x, and test each point.&lt;br /&gt;&lt;br /&gt;Finally, problem I describes a puzzle with 7 positions, 6 of which contain a letter and one position is blank. A valid move is to move a letter from its current position into the blank position. The goal is to convert the game into the winning state as described by the problem statement. The problem states that given an initial configuration, print out the minimum number of moves along with the solution to reach the win state or print NO SOLUTION if it is not possible.&lt;br /&gt;&lt;br /&gt;This is a simple example of a "&lt;a href="http://seanfalconer.blogspot.com/2009/11/searchy-type-problems.html"&gt;Searchy type problem&lt;/a&gt;" that I had blogged about before. The positions can be represented as nodes in a graph, edges exist between the nodes based on how the puzzle is described. You can then perform a breadth-first-search over the graph, where a new state for the queue is added for each valid movement of a letter to the current blank space. Also in the state, you need to keep track of the path you took to reach that state. Once you find the winning state, print the path and length.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-6157103940177859994?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/6157103940177859994/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2010/01/bc-winter-programming-contest-practice.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/6157103940177859994'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/6157103940177859994'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2010/01/bc-winter-programming-contest-practice.html' title='BC Winter Programming Contest Practice'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-1688485297543854784</id><published>2009-12-17T09:20:00.000-08:00</published><updated>2009-12-17T10:15:35.055-08:00</updated><title type='text'>How to setup and run your own programming contest</title><content type='html'>It's been a couple of weeks since my last post as there's been less going on with the programming teams lately due to exams.  This post is about how to run your own programming contest, but first, I thought I should mention that all the UVic team members were recognized and awarded Outstanding Leadership Awards last night at the computer science department Christmas party.  There's also been talk about establishing some kind of yearly high school programming competition in BC.  It would potentially be great for recruitment, be a good way to get young people interested in computer science, and hopefully keep the top BC students in BC.&lt;br /&gt;&lt;br /&gt;Ok, now onto the topic: how to setup and run your own programming contest.&lt;br /&gt;&lt;br /&gt;Before I get into the details, I'll just mention a few bad experiences I have had as a competitor.  You may not think organizing a contest would be that difficult, but it can be quite a disaster if there isn't much care put into it.  I use to compete in the &lt;a href="http://www.cs.rit.edu/%7Eicpc"&gt;Northeast North America &lt;/a&gt;regional.  This is one of the few regions in North America that has preliminary rounds.  Between all the contest sites, there is no consistency in the problem writing style or the submission software used.  Year to year, the contest submission software changes and not always for the better.&lt;br /&gt;&lt;br /&gt;In one particular preliminary contest, every team had to use a Windows system, but telnet into a Unix box, and use a single non-resizable terminal for coding and submitting.  Even more surprising was that the problems were not printed for the teams before the contest and teams had to read the problems directly on the machine.  We were told that we could print screen the problems piece by piece if we needed print outs. &lt;br /&gt;&lt;br /&gt;Since only one terminal was supported, you would of course be forced to switch between your code editor back to the single terminal for compiling, running, and submitting.  The submission software consisted of shell scripts, solution judgements were written into a file, which you would need to periodically check.&lt;br /&gt;&lt;br /&gt;Many of the input files had been created on a Windows machine and ported to Unix, which led to strange end of line and end of file characters.  Finally, there was a number of errors in both the judge's test cases and the problem specifications.  Not surprisingly, over half the teams solved nothing, and we came in second with a grand total of three problems solved.&lt;br /&gt;&lt;br /&gt;One of the other issues with the preliminary round was the problem writing inconsistency.  This may have been due to lack of experience from the problem writters or lack of care from the organizers, but each problem would usually be formatted completely different.  Some looked as if they were typed in Notepad, with no input and output specified, sometimes without any sample test cases.  Others may be a PDF file, written in Latex.  Usually if there was some description of the input and output, the constraints of the problem would not be specified.&lt;br /&gt;&lt;br /&gt;Now that the horror stories are complete, as for running your own contest, there are several options and it's not really that complicated, but is perhaps a little daunting for those unfamiliar with competing.  To start with my own experience, I've written problems for, acted as judge, organized, and ran about half a dozen contests, both at UVic and the University of New Brunswick (UNB).  Probably the most difficult part, besides getting students interested in coming out for such an event, is coming up with the problem set, judge's inputs and outputs, all without major errors.&lt;br /&gt;&lt;br /&gt;The goal with most ACM-style contests is to create a problem set such that every student/team solves at least one problem, no student/team solves all the problems, but all problems are solved by someone.  This is very tricky to pull off.  If the problems are too simple, then you may end up with a situation like this year's &lt;a href="http://acmgnyr.org/year2009/standings.shtml"&gt;Greater NY Regional&lt;/a&gt; where nine teams solved all the problems.  This is usually a sign of a poorly designed and potentially unfair contest.  It potentially rewards the fastest coders for simple problems rather than the best problem solvers.&lt;br /&gt;&lt;br /&gt;One of the major issues I've struggled with in any contest that I've run is the large variance in skill level between the few exceptional students I had access to and the less experienced students.  You don't want the more advanced students solving everything in the first two hours, but you don't want to make the problem set so difficult that the majority of the students leave frustrated and embarrassed.  I've been both successful and unsuccessful at achieving this balance. &lt;br /&gt;&lt;br /&gt;For example, in the last contest I ran at UNB, the top two students solved 6/7 problems, two students solved 2, two solved 1,  and everyone else solved nothing.  I felt quite bad judging this contest, and many of the students left frustrated.  However, the first UVic contest that I organized, had a much better balance (&lt;a href="http://webhome.cs.uvic.ca/%7Eseanf/contest/01312009/summary.html"&gt;scoreboard&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;In individual contests, I have usually tried to have between 6 and 7 problems, with at least one very simple problem to get everyone started on the right foot.  I also usually organize the problems roughly from easiest to hardest, as a lot of inexperienced people will start with the first problem regardless of its difficulty.  I also usually try to have the second or third easiest problem be something that is easy to understand conceptually, but may take some time to implement.  My goal here is to give less experienced students something to work on and potentially solved, but it will keep them busy for a while.  Finally, I try to mix in some shorter to code problems that require a bit of insight.&lt;br /&gt;&lt;br /&gt;If you are simply trying to set up a contest environment for team practice, then you can probably skip the problem writing stage.  If you go to the &lt;a href="http://cm.baylor.edu/public/report/regionalInfo.icpc?cid=12206"&gt;regional page&lt;/a&gt; on the ACM ICPC website and follow the links to various regionals, many will include their problem set, judges input and output, and solutions.  You can use these files to create your own practice contest environment.&lt;br /&gt;&lt;br /&gt;In all the contests that I've organized, I've used the &lt;a href="http://www.ecs.csus.edu/pc2/"&gt;PC^2 software&lt;/a&gt; for problem submission and judging.  I recommend using version 9, as it's much better with firewall issues.  PC^2 is quite simple to set up.  The software is written in Java, so it runs on most platforms.  It has several different programs, a server, scoreboard, judge, admin, and client.  You start by running the server, and all other programs connect through the server.  Configuration is as simple as modifying a config file to point to the server location.  The administration control panel is used for creating accounts, inputting problems, and configuring how the judgements will run.  Judging can be done automatically or semi-automatically. &lt;br /&gt;&lt;br /&gt;I always create a judge's set of input and output, and configure PC^2 with that information for each problem.  I also use automatic judging as a guide.  If the automatic judge gives me an accept, I usually take it.  If there's a different answer, I'll do a side by side comparison between the submitted program output and my judge's output just to make sure. &lt;br /&gt;&lt;br /&gt;The client software is what the students or teams use to submit their solutions.  They can select the problem to submit under, the language of the source code, and the source file.  Submissions are put into a queue on the judge's side.  The client software also supports clarification submissions, which are questions about the problem set that you can send to the judge.&lt;br /&gt;&lt;br /&gt;The scoreboard is very simple, it writes the current scores into different html files.  The summary.html is the best view.  The admin software allows you to configure when the scoreboard should be frozen, typically for the last hour or half hour in a contest.&lt;br /&gt;&lt;br /&gt;There are other contest systems out there.  &lt;a href="http://www.codechef.com/"&gt;CodeChef&lt;/a&gt;, which I wrote about in my &lt;a href="http://seanfalconer.blogspot.com/2009/11/how-can-i-compete.html"&gt;last post&lt;/a&gt;, will host contests for you.  One of the advantages of using PC^2 is that it is the system used both at the world finals and at a lot of the regional level contests so it exposes your team to the system early.  It is very simple to use, so it's probably not a huge gain, but it may help a bit :-).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-1688485297543854784?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/1688485297543854784/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/12/how-to-setup-and-run-your-own.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/1688485297543854784'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/1688485297543854784'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/12/how-to-setup-and-run-your-own.html' title='How to setup and run your own programming contest'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-5748388838198983079</id><published>2009-11-30T20:07:00.000-08:00</published><updated>2009-12-01T10:05:23.238-08:00</updated><title type='text'>How can I compete?</title><content type='html'>I sometimes get questions from people asking me about how to get into competition programming, how to practice, how to use the various problem archive websites and so forth, so I thought I'd write a blog explaining how it all works.  Most of the practice and competition sites out there have very good guides for this very thing, so this post will mostly give a short description of your various options, what I think of them, and it will hopefully be useful to those that are currently in the dark about this kind of thing.&lt;br /&gt;&lt;br /&gt;Before I get into describing the various online options, if you're in university or high school there is two main competitions that you could potentially get involved in.  For high school students this is the &lt;a href="http://en.wikipedia.org/wiki/International_Olympiad_in_Informatics"&gt;International Olympiad of Informatics&lt;/a&gt; (IOI).  I've never been involved with this competition, so I don't know a lot about it.  I do know that it is a world-wide competition and up to four individuals from a country can compete.  There's typically a national round first in order to choose a country's four members.  If you're in high school and have some programming skills it might be something to consider.  I'd first try finding some information about previous competitors from your country and find out how to get involved.&lt;br /&gt;&lt;br /&gt;University students can compete in the &lt;a href="http://cm.baylor.edu/welcome.icpc"&gt;ACM Intercollegiate Programming Competitions&lt;/a&gt; (ICPC).  This is the largest competition of its kind and the type of competition I've been primarily involved in as a competitor and coach.  Participants compete on teams of three with a single computer for five hours.  The teams that solve the most problems in the least amount of time win.  Recently, the top 100 teams from around the world meet to compete against each other in the world finals.&lt;br /&gt;&lt;br /&gt;To qualify for the world finals, you have to place high in a regional level competition.  The world is divided into around 40 regions.  In North America, usually you have to be one of the top two teams in the region to move onto the finals.  If you're in university, &lt;a href="http://cm.baylor.edu/public/report/regionalInfo.icpc"&gt;find your region&lt;/a&gt; and see if your school has a team.  If they don't, then I encourage you to get involved.  You just need a faculty member to sponsor you and hopefully they can help you get whatever funding is necessary for travel.&lt;br /&gt;&lt;br /&gt;Ok, great, you're sold on the idea of competing, so how to practice?  There's several great websites out there that can help.  Most of them follow a similar format; problem statement, with description of input and output formats, and several example test cases.  You submit to an online judging system, usually through a form, and the online judge compiles your source and runs the executable against a hidden set of test cases.  If you pass everything, you solved the problem. Be very careful when you start, you need to match the specified input and output formats exactly. Even if you are off by a period or whitespace, your answer will be wrong.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://uva.onlinejudge.org/index.php"&gt;UVA Online Judge&lt;/a&gt; - This site contains thousands of problems and an online judging system.  To use the site, simply register, then pick a problem, solve it offline on your own computer, and when you're satisfied, submit it through the UVA online submission form.  You'll get an email indicating whether the problem was accepted (yay!) or if your code produced a wrong answer, time limit exceeded, runtime error, or compile error.  The site also hosts online competitions, however, watch out for the timezone.  One disadvantage of the site is that the problems are not really organized by problem type, so it's hard to practice certain types of problems.  However, if you search the &lt;a href="http://online-judge.uva.es/board/viewforum.php?f=31&amp;amp;sid=39de9b59c6cdb019e5f30b06b6fcaf9f"&gt;message board&lt;/a&gt; for say "dynamic programming" you can often find someone listing problem numbers of this problem type.  The site supports C, C++, Java and Pascal solutions.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://acm.uva.es/archive/nuevoportal/"&gt;ACM Live Archive&lt;/a&gt; - This site is similar to UVA and run by, I believe, some of the same people.  It contains previous problems from many of the regional competitions and world finals.  Like UVA, you can submit solutions directly through a web form.  There's also a message board for finding help, although it's not nearly as populated as the UVA message board.  One major issue with this site is they only support Java 1.0, which basically means, they don't support Java&lt;span style="text-decoration: underline;"&gt;.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;a href="http://projecteuler.net/"&gt;Project Euler&lt;/a&gt; - I've known about this site for a while, but only recently started using it.  There's several nice things about the site.  The first is that the problems are listed in increasing difficulty, so problem 1 is suppose to be the easiest and the latest posted problem (currently 266) is the hardest.  The other really nice thing is that you only submit an answer to a problem, rather than source code.  This allows you to solve the problem in any language that you are comfortable with. It's a great way to experiment with learning a new language.  The problems tend to often be math oriented.  It's a great resource, but since it is a different style than the ACM or IOI competitions, it may not be as good a representation of how it is to compete in those particular competitions.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.codechef.com/"&gt;CodeChef&lt;/a&gt; - I just discovered this site after a friend sent me a link. It's a fairly new competition website, similar to the UVA online archive.  They have a competition every month, where 6 problems are posted and competitors have 10 days to try to solve them.  You earn 1 point for each solved problem.  There's also team competitions, but I am not sure how often they are organized.&lt;br /&gt;&lt;br /&gt;Problems are organized by easy, medium and hard.  There seems to be a great community contributing to the site.  The social aspects of the site are tightly integrated with the problem sets and contests in comparison to the older websites that simply have message boards.  One other nice thing is that it supports a wide range of languages.  Finally, if you do well, there's prize money :-).&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.topcoder.com/tc"&gt;TopCoder&lt;/a&gt; - This is one of the largest competition sites available. Anyone can compete, and there's typically two or three algorithm competitions a month (SRM - single round matches). Once you sign up, you can run the competition applet and try problems from previous competitions.  There's several key differences in comparison to ACM style competitions.&lt;br /&gt;&lt;br /&gt;First, there's two divisions, Division I and II, based on your membership rating.  You earn points by competing, people with ratings less than 1200 compete in Division II, everyone else is in Division I.  So, when you first sign up, you'll be in Division II.  If you do poorly in a competition relative to other people with similar ranking to you, then your rating can actually drop.&lt;br /&gt;&lt;br /&gt;Second, there's always 3 problems, each with different point values. The faster you solve a problem, the more points you get. There's an easy, medium, and hard problem, the more difficult the more points you can potentially get.  Third, unlike ACM contests where after you submit you get a judgement about your problem, on TopCoder, submissions are not judged until after the competition.  If you decide to re-submit, you lose 10% of your original point value. Unlike, ACM contests, you do not have to worry about writing input and output parses based on some described format.  Typically, you are asked to write a specific class with a method that takes certain parameters and returns a certain parameter. &lt;br /&gt;&lt;br /&gt;Finally, contests only last an hour and 15 minutes. After the coding part of a competition, there's a 10 minute break, and then a challenge phase.  During the challenge phase, you get to look at other people's source code and challenge their solutions. If you make their code fail, you get extra points and they lose whatever points they would have potentially scored.  Once this phase is complete, the judge's hidden input cases are run against all submitted programs.  If you fail any cases, you lose whatever points you accumulated for that problem.&lt;br /&gt;&lt;br /&gt;There's several great things about training on TopCoder.  There's lots of competitions, so it gets you use to competing. Because you really only get a single submission, it can help you improve your accuracy. You can see other people's source code, so it can be a great way to learn how to solve a problem or learn a better way to solve a problem.  There's also editorials after each SRM describing how to solve the problems.  The problem archive is nicely organized and searchable based on different criteria like difficulty and problem type (DP, greedy, search, etc).  Also, if you are very talented, you can earn a substantial amount of money competing on TopCoder.  Many contests and tournaments are organized by companies as a way to recruit potential employees.&lt;br /&gt;&lt;br /&gt;This post is already quite long, so hopefully that's enough to get any newbies out there pointed in the right direction.  Next time, I'll post about how to run your own competition and provide some suggestions for how to get things going at your own school.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-5748388838198983079?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/5748388838198983079/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/11/how-can-i-compete.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/5748388838198983079'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/5748388838198983079'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/11/how-can-i-compete.html' title='How can I compete?'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-7098848850616716229</id><published>2009-11-26T11:11:00.000-08:00</published><updated>2009-11-27T08:34:55.986-08:00</updated><title type='text'>Searchy type problems</title><content type='html'>A lot of problems in the ACM and TopCoder competitions can be solved using a general search technique like breadth-first search (BFS).  Many BFS type problems are represented with an initial grid and you want to determine whether you can achieve a certain state or grid configuration, possibly in the minimum number of moves.  These problems are very popular and can vary in complexity, however, the general problem solving principles remain the same.  &lt;br /&gt;&lt;br /&gt;You can usually determine whether BFS is the right approach for a problem of this sort by first checking the bounds of the grid.  Most of the time, the grid will be less than 20x20.  Secondly, you need to determine how to represent the current state of the problem.  You can think of this state as a node in a graph.  Finally, you need to determine if one state can be transformed into a subsequent and valid state through a move with a cost of one.  You can think of these moves as edges between your nodes.  Another hint is that many of these problems can be classified as single player game problems.&lt;br /&gt;&lt;br /&gt;Let's consider an example.  In the problem &lt;a href="http://uva.onlinejudge.org/index.php?option=com_onlinejudge&amp;Itemid=8&amp;category=19&amp;page=show_problem&amp;problem=1645"&gt;Traffic!&lt;/a&gt;, a simple game is described where are given an initial puzzle configuration consisting of blocks of varying sizes.  The goal of the game is to figure out how to remove the white piece by moving the various puzzle pieces and repositioning the white piece so that it slides through the opening in the puzzle.  In the problem you are asked to find the minimum number of moves to accomplish this task.&lt;br /&gt;&lt;br /&gt;So, let's go back to our criteria for determining whether BFS is a valid approach.  First, the grid is of size 6x6, so that's well within our heuristic 20x20 bound. Second, the state of the problem can be represented as a list of the current locations for each puzzle piece.  A move in the game consists of moving a single puzzle piece any number of open positions horizontally or vertically.  Horizontal pieces can only move horizontally and vertical pieces can only move vertically.  Sliding a horizontal piece one spot to the right has the same cost as moving it two spots to the right, that is, it counts as one single move.  Thus, we can transition from one state of the board to another with a cost of one. Finally, the problem even fits the format of being described as a single player game.&lt;br /&gt;&lt;br /&gt;Once we've established that we can solve the problem using BFS, then the algorithm to actually solve the problem is quite straight-forward.  A generic approach could be something similar to the following code:&lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;int solve(State start) {&lt;br /&gt;  set&amp;lt;State&amp;gt; visited;&lt;br /&gt;  queue&amp;lt;State&amp;gt; q;&lt;br /&gt;&lt;br /&gt;  q.push(start);&lt;br /&gt;&lt;br /&gt;  while(!q.empty()) {&lt;br /&gt;    State s = q.top(); q.pop();&lt;br /&gt;    // skip states we've already processed&lt;br /&gt;    if(visited.find(s) != visited.end()) continue;&lt;br /&gt;&lt;br /&gt;    // we reached our goal, return moves&lt;br /&gt;    if(goalStateReached(s)) return s.moves;&lt;br /&gt;&lt;br /&gt;    // visit the state&lt;br /&gt;    visited.insert(s);&lt;br /&gt;&lt;br /&gt;    // try all valid moves &lt;br /&gt;    // and add them to our queue&lt;br /&gt;    enqueueValidMoves(q, s);&lt;br /&gt;  }&lt;br /&gt;  // never reached the goal&lt;br /&gt;  return -1;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;In this case, State is going to consist of a list of the current block locations, the number of moves it took to achieve this state, and a less than operator for storing the state properly within our visited set.  The enqueueValidMoves will loop over the positions in our state object and create a new state for any valid vertical and horizontal moves.  We know we reached the goal state when the white piece is located in column 6.&lt;br /&gt;&lt;br /&gt;There are a few simple tips for working on problems of this type or grid representations in general. First, let's consider a simple 0/1 matrix where from any cell i,j, containing a one, you can move east, west, north or south provided the move takes you to a cell containing a one:  &lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;110&lt;br /&gt;010&lt;br /&gt;111&lt;br /&gt;&lt;/pre&gt;So, the cell 0,0 can only move to cell 0,1 as this is its only neighbor containing a one. We could potentially perform a BFS where we check for valid moves as follows:&lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;while(!q.empty()) {&lt;br /&gt;  // queue consists of pairs of integers &lt;br /&gt;  // representing cell locations&lt;br /&gt;  pair&amp;lt;int, int&amp;gt; p = q.top(); q.pop();&lt;br /&gt;&lt;br /&gt;  if(visited[p]) continue;&lt;br /&gt;&lt;br /&gt;  // process potential moves&lt;br /&gt;  if(p.first-1 &amp;gt;= 0 &amp;&amp; grid[p.first-1][p.second] == 1)  &lt;br /&gt;    // enqueue item&lt;br /&gt;  if(p.first+1 &amp;lt; rowCount &amp;&amp; grid[p.first+1][p.second] == 1) &lt;br /&gt;   // enqueue item&lt;br /&gt;  if(p.second-1 &amp;gt;= 0 &amp;&amp; grid[p.first][p.second-1] == 1) &lt;br /&gt;   // enqueue item&lt;br /&gt;  if(p.second+1 &amp;lt; colCount &amp;&amp; grid[p.first][p.second+1] == 1) &lt;br /&gt;   // enqueue item&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;That will work, but all the bounds checking and if statements are kind of ugly and error prone.&lt;br /&gt;&lt;br /&gt;To clean up this code a bit, one simple trick is to remove bound checks by padding the grid with empty cells. For example, our grid from above would become:&lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;00000&lt;br /&gt;01100&lt;br /&gt;00100&lt;br /&gt;01110&lt;br /&gt;00000&lt;br /&gt;&lt;/pre&gt;Now, our check just needs to verify whether a given cell contains a value of one.&lt;br /&gt;&lt;br /&gt;Another simple tip when processing moves over a grid, like north, south, east and west transitions is to store these moves as x, y and possibly z deltas in an array.  Thus, with those two simple tricks combined, our messy code above becomes:&lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;int x[] = {1, 0, -1, 0};&lt;br /&gt;int y[] = {0, 1, 0, -1};&lt;br /&gt;while(!q.empty()) {&lt;br /&gt;  // queue consists of pairs of integers representing cell locations&lt;br /&gt;  pair&lt;int, int&gt; p = q.top(); q.pop();&lt;br /&gt;&lt;br /&gt; if(visited[p]) continue;&lt;br /&gt;&lt;br /&gt; // process potential moves&lt;br /&gt; for(int i = 0; i &amp;lt; 4; i++) {&lt;br /&gt;   int r = p.first+x[i];&lt;br /&gt;   int c = p.second+y[i];&lt;br /&gt;   if(grid[r][c] == 1) // enqueue move&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;It seems like a pretty simple change, and I'm sure some will think it's not worth it, especially for this simple example where there's only four possible move directions, but once you start allowing moves along the diagonal you are up to having 8 if statements to write.  The more code you have to write, especially with only slight modifications like adding one here and subtracting one there, the more probable it is that you will make a mistake that will cost you debugging time.&lt;br /&gt;&lt;br /&gt;Other problems of this type:&lt;br /&gt;&lt;a href="http://acm.uva.es/p/v4/439.html"&gt;439 - Knight Moves&lt;/a&gt;&lt;br /&gt;&lt;a href="http://uva.onlinejudge.org/index.php?option=com_onlinejudge&amp;Itemid=8&amp;category=10&amp;page=show_problem&amp;problem=782"&gt;841 - Snake&lt;/a&gt;&lt;br /&gt;&lt;a href="http://acm.uva.es/p/v5/589.html"&gt;589 - Pushing Boxes&lt;/a&gt;&lt;br /&gt;&lt;a href="http://acm.uva.es/p/v107/10793.html"&gt;10793 - The Orc Attack&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-7098848850616716229?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/7098848850616716229/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/11/searchy-type-problems.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/7098848850616716229'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/7098848850616716229'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/11/searchy-type-problems.html' title='Searchy type problems'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-7198985379080144717</id><published>2009-11-24T10:29:00.000-08:00</published><updated>2009-11-24T11:27:30.191-08:00</updated><title type='text'>No Finals for UVic</title><content type='html'>I received confirmation last night that we did not make the 2010 programming finals.  The Pacific Northwest region only received two spots this year, instead of the three it received the last two years.  Only one region in North America, East Central NA, received three spots. That region had over 100 teams, while our region only had 82.  Also, the top four teams solved all 8 problems, whereas UVic was two problems behind Stanford.&lt;br /&gt;&lt;br /&gt;Despite the sound reasoning behind the choice, it's quite disappointing, especially since our expectation was to make it.  Goes to show these wild card spots are never for sure.  I'm confident that the team will have a very good chance to finish in the top two at regionals next year. I believe both UBC and Stanford are losing some of their key members, while our team will be the same, but with one more year of experience.  &lt;br /&gt;&lt;br /&gt;The guys have taken the news incredibly well (far better than me).  They are still fired up to keep practicing and get ready for next year.  Unfortunately, this is my last official term working at UVic, so I won't be physically at the university next Fall to keep things going.  I am starting a job at Stanford University (I know, the enemy) in February.  I am going to attempt staying involved with the UVic teams remotely.  I think I can still organize practices, pick problems, and help provide feedback on problems all from California.  Also, if I'm involved with the Stanford teams, I can simply include the UVic folks in the practices.&lt;br /&gt;&lt;br /&gt;Finally, despite not making the finals, both teams did incredibly well.  We also managed to generate quite a bit of buzz at the university about the ACM ICPC competitions, which should help with funding next year.  Hopefully more students will become interested and want to join as well.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-7198985379080144717?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/7198985379080144717/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/11/no-finals-for-uvic.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/7198985379080144717'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/7198985379080144717'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/11/no-finals-for-uvic.html' title='No Finals for UVic'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-266950984974361374</id><published>2009-11-20T08:47:00.000-08:00</published><updated>2009-11-20T08:59:30.347-08:00</updated><title type='text'>Competitive Programming on TV?</title><content type='html'>The UVic programming teams have recently been receiving some media coverage.  &lt;a href="http://www.bclocalnews.com/vancouver_island_south/saanichnews/news/70407052.html"&gt;Saanich News&lt;/a&gt; did an article on the guys and UVic released a &lt;a href="http://communications.uvic.ca/releases/tip.php?date=16112009"&gt;small media tip&lt;/a&gt; about our performance at the Pacific Northwest regional competition a few weeks ago.&lt;br /&gt;&lt;br /&gt;Also, at our last practice, &lt;a href="http://www.atv.ca/victoria/"&gt;A Channel News&lt;/a&gt; came and filmed some of the session as well as interviewed myself and some of the guys on the team.  They put together a really nice piece on us, which aired last night.  They took kind of a sports angle on the ACM ICPC programming competitions.  Will competitive problem solving be the next big hit reality TV show?  (umm, probably not :-)&lt;br /&gt;&lt;br /&gt;I thought the piece was really well done.  &lt;a href="http://www.atv.ca/victoria/personalities_JordanCunningham.aspx"&gt;Jordan Cunningham&lt;/a&gt; put it together and did a fantastic job.  A friend of mine was nice enough to record the broadcast and put it online.  You can check it out below.&lt;br /&gt;&lt;br /&gt;&lt;object width="400" height="300"&gt;&lt;param name="allowfullscreen" value="true" /&gt;&lt;param name="allowscriptaccess" value="always" /&gt;&lt;param name="movie" value="http://vimeo.com/moogaloop.swf?clip_id=7720044&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" /&gt;&lt;embed src="http://vimeo.com/moogaloop.swf?clip_id=7720044&amp;amp;server=vimeo.com&amp;amp;show_title=1&amp;amp;show_byline=1&amp;amp;show_portrait=0&amp;amp;color=&amp;amp;fullscreen=1" type="application/x-shockwave-flash" allowfullscreen="true" allowscriptaccess="always" width="400" height="300"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;p&gt;&lt;a href="http://vimeo.com/7720044"&gt;University of Victoria Computer Programming Team&lt;/a&gt; from &lt;a href="http://vimeo.com/user2666359"&gt;Donald Peterson&lt;/a&gt; on &lt;a href="http://vimeo.com"&gt;Vimeo&lt;/a&gt;.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;On the practice front, we studied a variety of dynamic programming problems at Wednesday night's practice and I've been organizing a fairly large practice for this Sunday.  Stanford is hosting the competition and I've been coordinating with their coach about problems and various invited teams.  I'm very excited about this practice as I actually managed to get some Eastern North American teams to join.  McGill, University of Rochester, and St. Mary's have all confirmed that they are joining and I have sent invites to MIT, Dalhousie, UNB, and Mount Allison University.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-266950984974361374?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/266950984974361374/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/11/competitive-programming-on-tv.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/266950984974361374'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/266950984974361374'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/11/competitive-programming-on-tv.html' title='Competitive Programming on TV?'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-2963726518578820828</id><published>2009-11-16T12:50:00.000-08:00</published><updated>2009-11-18T12:20:40.337-08:00</updated><title type='text'>2010 World Finals Preparation</title><content type='html'>Still no word on whether the UVic Whites have made the 2010 ACM ICPC world programming finals in Harbin, China, but we're going to forge ahead with practices as if we have made it.  Based on everything I could research, we have a very good chance.  In the last two years, there's been 100 teams at the finals, and both times there's been three from the Pacific Northwest region.  UBC^ was in the exact same position as us last year and they went, so I am confident we'll make it.&lt;br /&gt;&lt;br /&gt;I gave everyone the week off last week, however the guys finished off all the problems from last week's regional on their own.  Dan also re-coded his solution to problem D using a line sweep algorithm rather than the more direct but less efficient N^3 solution that he submitted during the competition.&lt;br /&gt;&lt;br /&gt;As for world finals preparation, there's some key areas that the guys need to work on: dynamic programming, search problems, hard geometry problems, and coding speed.  I've started compiling a list of these kinds of problems.  We'll also spend some time going over previous world finals.  I've also started compiling a list of algorithms and techniques that guys should learn.&lt;br /&gt;&lt;br /&gt;For example:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;   Weighted matching, assignment problem, hungarian method&lt;/li&gt;&lt;li&gt;Linear programming, Pick's theorem&lt;/li&gt;&lt;li&gt;Advanced geometry techniques (line sweeping, convex hull, 3D convex hull)&lt;/li&gt;&lt;li&gt;String matching (KMP)&lt;/li&gt;&lt;li&gt;Number theory: primitive roots, extended euclid's algorithm, prime numbers&lt;/li&gt;&lt;/ol&gt;I plan to continue having two practices a week, Wednesday evenings and Saturday or Sunday.  Wednesday's will be for skill development while the weekend practice will be for practice competitions, hopefully with some other Canadian teams.  I will be away over Christmas to early January, so the guys will need to practice on their own then.  Also, at least one of the team members will be in another city starting next term for a co-op so full team practices in January will probably be out of the question.&lt;br /&gt;&lt;br /&gt;I plan to spend the next two Wednesday's working on the team's most obvious weak area: dynamic programming(DP)/memoization.  They tend to shy away from these problems and have convinced themselves that they aren't very good at them.  I think they just need more practice.&lt;br /&gt;&lt;br /&gt;Many DP problems have very distinct patterns that give the solver clues about what to memoize or DP on.  For example, recently in one of the team's practice competitions there was a problem where you had to figure out the minimum number of days that you could assign several different allergens for testing (see &lt;a href="http://acm.tju.edu.cn/toj/showp3447.html"&gt;problem statement&lt;/a&gt;).  An allergen can be assigned at 8am every morning and symptoms for the allergen can be detected by 8pm at night. However, if there is more than one allergen in the body, you can't uniquely identify the allergen causing the issue.  Different allergens take different numbers of days to work their way out of a body, maximum of 8 days.  You have to figure out how to assign the allergens (at most 20) such that all of them are detected, but such that the total number of days is minimized.&lt;br /&gt;&lt;br /&gt;Let's take a simple example.  Consider allergens that take 1, 2, 3 and 7 days.  One solution could be to assign the allergen that takes 7 days first, then the one that takes 3, then 2, and finally 1.  So, the first allergen is assigned and detected the first day.  We can't assign the second allergen until the 6th day, and it will be detected the 8th day.  Now we can assign the allergen taking 2 days, wait for it to run its course, and finally assign the last allergen.  The total number of days is 11.&lt;br /&gt;&lt;br /&gt;Alternatively, we could assign the allergen taking 2 days first, then the 7, 3, and 1, which only takes 10 days.&lt;br /&gt;&lt;br /&gt;Just looking at the problem, it's tempting to perhaps try greedy, but as you can see from the simple example I just went over, it's not going to work.  The other hint that greedy is probably the wrong approach is that there's only 20 allergens.  If greedy worked, there could be 100 or perhaps 1000 allergens.&lt;br /&gt;&lt;br /&gt;The fact that it's a minimization problem and that there are only 20 allergens are big hints that DP is the way to go.  Furthermore, 20 is a curiously small number, which gives us a hint about a potential parameter to DP on.  For DP, we want to compute optimal sub-problems and use those to figure out the solution to the whole problem.  The sub-problem here is the minimum number of days for the assignment of X allergens.  We can represent all allergen assignments in binary, where a number like 100101 means we've assigned the first, third, and last allergen so far. &lt;br /&gt;&lt;br /&gt;When we apply a new allergen to an existing solution, we need to know how many new days we are adding to the existing solution.  The only information we need in order to solve this problem is to know how many days in the existing solution the last applied allergen is active for.  Together with our allergen assignments and the number of days the last applied allergen is active for, we can represent any potential solution.  The solution would look something like the following (initialization code is not shown):&lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;// allergens, D[0] is the number of days allergen 0 is active for&lt;br /&gt;int D[21];&lt;br /&gt;// DP array, all assignments by days last allergen is active&lt;br /&gt;int M[1&lt;&lt;20+1][8];&lt;br /&gt;&lt;br /&gt;// number of allergens in the current case&lt;br /&gt;int k;&lt;br /&gt;&lt;br /&gt;// try all possible allergen assignments&lt;br /&gt;for(int i = 1; i &amp;lt; (1 &amp;lt;&amp;lt; k); i++){&lt;br /&gt;  // try assigning allergen j&lt;br /&gt;  for(int j = 0; j &amp;lt; k; j++) {&lt;br /&gt;    // only try j if it's not in the solution&lt;br /&gt;    if(i&amp;(1&amp;lt;&amp;lt;j)){&lt;br /&gt;      // try all days active options&lt;br /&gt;      for(int n = 0; n &amp;lt; 8; n++) {&lt;br /&gt;        // days active for j&lt;br /&gt;        int days_active = max(D[j] - n - 1, 0);&lt;br /&gt;        // solution without j&lt;br /&gt;        int t = i ^ (1 &amp;lt;&amp;lt; j);&lt;br /&gt;        // keep track of best answer&lt;br /&gt;        M[i][days_active] = min(M[i][days_active], &lt;br /&gt;              M[t][n] + days_active);&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;All the best solutions involving all allergens will be stored at M[1&amp;lt;&amp;lt;20-1][i] for i=0 up to i=8.  You simply need to take the minimum one as your answer.&lt;br /&gt;&lt;br /&gt;There are many problems that can be solved similarly.  If you do enough of these DP problems, you start to see the tricks faster.  I find sometimes drawing out the call tree or drawing out dependencies between sub-problems as a graph can be an effective means for seeing the overlap between sub-problems.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-2963726518578820828?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/2963726518578820828/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/11/2010-world-finals-preparation.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/2963726518578820828'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/2963726518578820828'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/11/2010-world-finals-preparation.html' title='2010 World Finals Preparation'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-1755446052429485384</id><published>2009-11-09T09:25:00.001-08:00</published><updated>2009-12-04T09:48:34.058-08:00</updated><title type='text'>2009 Pacific Northwest Regional Programming Competition</title><content type='html'>The 2009 &lt;a href="http://www.acmicpc-pacnw.org/"&gt;Pacific Northwest Regional Programming Competition&lt;/a&gt; happened this past Saturday. The Pacific Northwest region consists of teams from universities in BC, Washington State, Oregon, Hawaii, and northern California.  Approximately 80 teams took part this past weekend.&lt;br /&gt;&lt;br /&gt;We left Friday afternoon to travel to Vancouver in a van that I rented.  I felt a bit like a soccer mom, taking her 6 kids to a big tournament :-).  We stayed at a hostel in a rather sketchy section of Granville Street.  After a nice dinner and a horrible night of sleep, we were up at 7am Saturday morning to drive over to UBC for team registration.  Everyone was tired and to make matters worse, the competition was delayed an hour.&lt;br /&gt;&lt;br /&gt;I was extremely worried about our second team, Vikes Blue, because during the practice competition they were struggling with some basic input and output issues. Going into the competition, I had hoped that Vikes White would finish in the top 10 and the Blues would be in the top half.  Both teams far exceeded my expectations and completely blew me away with their performances under pressure.&lt;br /&gt;&lt;br /&gt;UVic's top team, Vikes White, consisting of second year students John Hawthorn, Scott Porter, and Dan Sanders finished 4th with 7 problems solved behind two teams from Stanford University and the winning team from UBC. The second UVic team, Vikes Blue, consisting of Tyler Cadigan, Tim Song, and Tristin Sturgess, finished 22nd with 5 problems solved.  The final scoreboard is available here: &lt;a href="http://130.86.76.240/pacnw09s/"&gt;http://130.86.76.240/pacnw09s/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This is only UVic's second time competing in the regional competition in recent years and only one student had competed before.  The Whites were in 4th place after 30 minutes with two problems solved, however, quickly fell to 9th as other teams solved their 3rd and 4th problem.  The Whites caught up by solving 3 more problems, and were tied with several teams in the top 10 with 5 problems solved by the 2 hour mark.  At the half way point in the competition, UBC and Stanford were on top with 7 problems solved and all other teams had 5 or less.&lt;br /&gt;&lt;br /&gt;Around this time, I could see my team high fiving and I got excited that perhaps they had submitted another problem.  I kept hitting refresh on the scoreboard, but no submissions came up.  I tried to look at their screen to see if they were submitting, but all I could see was Dan still working away.  Shortly afterwards, they submitted.&lt;br /&gt;&lt;br /&gt;The coaches receive the problem set at the start of the competition, and after reading through the problems, all the coaches agreed that problem D was by far the hardest problem.  This problem involved computing reflections of a laser through multiple crystals. It was the only problem I really had no idea how to solve.&lt;br /&gt;&lt;br /&gt;After the Whites submitted, the problem that came up on the scoreboard as under judgement was this very problem and I got extremely worried.  I couldn't believe they were attempting D when several other easier problems still existed.  The coach from SFU actually asked me, "Are your guys really gifted at geometry?"  I replied by saying, "They're ok."  They had never successfully solved a hard geometry problem in the past.&lt;br /&gt;&lt;br /&gt;I kept refreshing like mad to see what the judgement was, but before the scoreboard updated, I saw my guys high fiving again and I knew they must have received an accepted on it.  The scoreboard updated and they moved into 4th place behind two Stanford teams and UBC.  I couldn't believe, I was blown away.  At this point, they really had a chance to make the finals.&lt;br /&gt;&lt;br /&gt;However, during the next hour and a half, the team failed to submit anything and SFU, U. of Washington and Berkeley all caught up by solving 6 problems.  The Whites were still on top of this heap as they had received zero penalty minutes.  I knew they would most likely need a 7th or possibly 8th problem in order to keep their position.&lt;br /&gt;&lt;br /&gt;The scoreboard froze with one hour to go and during that time, unknown to the Whites and me, two teams from the University of Washington solved a 7th problem and moved into 4th and 5th.  The Whites struggled to get a 7th problem and I watched anxiously from the coaches room.  I could see them switching between Dan and John at the terminal and I was worried that they were trying to do too much.  However, with 5 minutes left in the competition, I saw them high fiving again, and I knew they had solved a 7th.  John moved onto the terminal immediately and was working away.  He submitted in the last minute, but received a TLE.&lt;br /&gt;&lt;br /&gt;We knew the team would probably be in the top 5, but they needed to be 4th behind the two Stanford teams and UBC in order to have a chance for the finals.  After dinner they announced the top 7 positions, and UVic had indeed moved into 4th.  They have a chance for a wild card position in the finals, but we won't find out until probably the end of the month.&lt;br /&gt;&lt;br /&gt;They were very close on two other problems, and were one of only two teams in the top 10 to receive no penalties for wrong answers.  They had accidentally typed in one of the input cases for B incorrectly and spent a long time debugging something that wasn't actually a bug in their solution.  This cost them considerable time.  They had solved G, but ran out of time to complete the implementation, which would have took them to 9 problems solved.  This would not have changed their overall position with regards to consideration for the world finals.  Luckily this lesson didn't cost them too badly and they'll hopefully not make the same mistake again.&lt;br /&gt;&lt;br /&gt;I was extremely proud and ecstatic with both teams performance.  I couldn't believe they both did so well.  I knew they were capable, but you never know how things will go and our practices of late have not been as smooth as I had hoped.  Both teams worked extremely well together and I hope we get the opportunity to go to the finals.  I've already started planning our practices to get better at some of the team's weak areas.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-1755446052429485384?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/1755446052429485384/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/11/2009-pacific-northwest-regional.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/1755446052429485384'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/1755446052429485384'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/11/2009-pacific-northwest-regional.html' title='2009 Pacific Northwest Regional Programming Competition'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-6731924500764212106</id><published>2009-11-04T17:07:00.000-08:00</published><updated>2009-11-04T17:28:51.564-08:00</updated><title type='text'>Last practice before the regional</title><content type='html'>We're having our last practice session tonight before the &lt;a href="http://www.acmicpc-pacnw.org/"&gt;Pacific Northwest Regional&lt;/a&gt; competition happening this weekend.  Our last few practices have not run as smoothly as I had hoped.  After our moderate success in the ACPC competition a few weeks ago (&lt;a href="http://seanfalconer.blogspot.com/2009/10/alberta-collegiate-programming.html"&gt;see October 18th post&lt;/a&gt;), I had been hoping to try out some new team strategy ideas.  However, students have been ill or have had other commitments so it's been difficult to have practices with everyone present.&lt;br /&gt;&lt;br /&gt;Tonight I'm having the students work on the problems from last weekend's &lt;a href="http://org.mesastate.edu/acm/rmrc/2009/index.html"&gt;Rocky Mountain Regional&lt;/a&gt;. The University of Alberta won this event by solving 7 of 9 and the University of Arizona was second with 5 problems solved.  I'm hoping in the 2.5 to 3 hour practice we have tonight that the guys are able to solve 5 problems.&lt;br /&gt;&lt;br /&gt;We leave Friday afternoon for Vancouver and the competition starts at noon (PST) on Saturday at UBC.  I'll be &lt;a href="http://twitter.com/seanfalconer"&gt;tweeting&lt;/a&gt; about the competition as it happens.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-6731924500764212106?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/6731924500764212106/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/11/last-practice-before-regional.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/6731924500764212106'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/6731924500764212106'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/11/last-practice-before-regional.html' title='Last practice before the regional'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-4797065799088042699</id><published>2009-10-22T10:07:00.000-07:00</published><updated>2009-10-22T11:42:34.955-07:00</updated><title type='text'>October 21st Practice Session</title><content type='html'>Last night we had our weekly Wednesday night practice session.  Based on results from previous competitions, I've noticed that typically our top performing team is one problem short of a top tier spot in the contest.  Also, in the &lt;a href="http://www.cs.ualberta.ca/%7Econtest/ACPC2009/contest/"&gt;ACPC competition from last Saturday&lt;/a&gt;, UVic White solved 8 problems without any errors, but they had some large gaps between submissions.  As a result, we decided to try to work on coding speed.&lt;br /&gt;&lt;br /&gt;Everyone was given a set of 8 easy to medium problems selected from the last 2 years of &lt;a href="http://ugweb.cs.ualberta.ca/%7Eacpc/2008/"&gt;ACPC.&lt;/a&gt;  They had to work as individuals and try to solve them as quickly as possible.&lt;br /&gt;&lt;br /&gt;Unfortunately, the UVA online judge was down, so I wasn't able to test their solutions until today.  Also, unfortunately, the results were not great.  There were quite a few wrong answers and time limit exceeded.  Some of these issues were due to minor oversights, but a few were due to the use of incorrect approaches for the problems.&lt;br /&gt;&lt;br /&gt;The one problem that no one successfully solved was &lt;a href="http://uva.onlinejudge.org/index.php?option=com_onlinejudge&amp;amp;Itemid=8&amp;amp;category=27&amp;amp;page=show_problem&amp;amp;problem=2631"&gt;Partitioning the Palindromes&lt;/a&gt;.  This was the trickiest problem in the set. &lt;br /&gt;&lt;br /&gt;In this problem, you are given a single string, and you want to partition it into subsets, where each subset is a palindrome and you want to do this such that the number of subsets is minimized.  Also, a valid subset has to be a substring of the original string, you can't move letters around.&lt;br /&gt;&lt;br /&gt;As an example, consider the string "aaadbccb".  You could potentially partition this into individual letters {"a", "a", "a", "d", "b", "c", "c", "b"}.  However, this leads to 8 subsets, you can do better by building larger palindromes, i.e. {"aaa", "d", "bccb"}.&lt;br /&gt;&lt;br /&gt;To solve this problem, you could potentially do backtracking, but if you look at the contraints, the length of the original string can be up to 1000 characters, which means backtracking will be way too inefficient.  This usually leaves one of two choices, a greedy approach or dynamic programming.&lt;br /&gt;&lt;br /&gt;It's easy to see that greedy will not work.  Consider the string "abbadabb". With greedy, you could choose the largest palindrome that you can make with the first letter, "abba".  Then the next two letters must be in their own subsets and finally the "bb" forms the last subset.  However, the optimal solution is to split the string into subsets {"a", "bbadaabb"}.&lt;br /&gt;&lt;br /&gt;One way to start to think about this problem is to think about the palindrome subsets as nodes in a graph.  For example, let's take the string "aaadbccb".  Originally, starting on the left hand side of the string, we can form palindromes "a", "aa", and "aaa".  Consider those as three different nodes in our graph.  If we start with node "a", what nodes could we get to from here?  Well, basically any palindrome starting with the second letter of our string, so this only includes palindrome "a" and "aa".  We can do the same type of expansion for the original "aa" and "aaa" nodes.  If we continue this expansion, we get a graph like the one below.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_rZy4UM398G0/SuCeMtuYiLI/AAAAAAAAAAU/qPbq1k164JU/s1600-h/pal_call_graph.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 201px; height: 400px;" src="http://1.bp.blogspot.com/_rZy4UM398G0/SuCeMtuYiLI/AAAAAAAAAAU/qPbq1k164JU/s400/pal_call_graph.png" alt="" id="BLOGGER_PHOTO_ID_5395486294859614386" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Notice how the shortest path through the graph represents the minimal subset of palindromes.  So, we could possibly do a breadth-first search, adding nodes to a queue and stopping when we complete the string.  However, this, like backtracking, will be too slow.&lt;br /&gt;&lt;br /&gt;The key insight that you need is to realize that a lot of these branches are useless to process.  There's a lot of duplicate construction, or overlapping construction, which generally means memoization or dynamic programming is a good approach.&lt;br /&gt;&lt;br /&gt;The way you can use this idea in this particular problem is to keep track of the minimum number of subsets used to create a string of length L.  You only process a particular node expansion if you can improve this cost, otherwise, you should cut the branch as you can't possibly do better than what you have already computed.  You could do this recursively or do it BFS style with a queue.  In semi-pseudocode, the BFS solution looks like the following:&lt;br /&gt;&lt;pre name="code" class="Cpp"&gt;// create array of min subsets, set values to infinity&lt;br /&gt;int M[1001];&lt;br /&gt;queue q;&lt;br /&gt;  &lt;br /&gt;int minSubsets(string s) {&lt;br /&gt;  addNeighbors(s, 0, 0, 0);&lt;br /&gt;  &lt;br /&gt;  while(q is not empty) {&lt;br /&gt;    Node p = q.dequeue();&lt;br /&gt;&lt;br /&gt;   if(p.stringSize == length(s)) return p.subsetSize;&lt;br /&gt;      &lt;br /&gt;    addNeighbors(p.startIndex, p.stringSize, p.subsetSize);&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;  &lt;br /&gt;// add any palindromes that extend the current subset&lt;br /&gt;void addNeighbors(int startIndex, int stringSize, int subsetSize) {&lt;br /&gt;   for(int i = startIndex+1; i &amp;lt; length(s)+1; i++) {&lt;br /&gt;      int stringLength = stringSize+i-startIndex;&lt;br /&gt;      // make sure the new subset is better than a previous one&lt;br /&gt;      // for the given string length&lt;br /&gt;      if(M[stringLength] &amp;gt; subsetSize+1 &lt;br /&gt;          &amp;&amp; isPalindrome(substring(s, startIndex, i))) {&lt;br /&gt;&lt;br /&gt;          M[stringLength] = subsetSize+1;&lt;br /&gt;          Node n(startIndex, stringLength, subsetSize+1);&lt;br /&gt;          q.enqueue(n);&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;There are many problems that can be solved using a similar method.  For example, both of these problems from the 2007 Pacific Northwest Regional competition can be solved this way with a few other tricks added in: &lt;a href="http://acmicpc-live-archive.uva.es/nuevoportal/data/problem.php?p=3995"&gt;Pebbles&lt;/a&gt; and &lt;a href="http://acmicpc-live-archive.uva.es/nuevoportal/data/problem.php?p=4074"&gt;Bridged Marble Rings&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-4797065799088042699?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/4797065799088042699/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/10/october-21st-practice-session.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/4797065799088042699'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/4797065799088042699'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/10/october-21st-practice-session.html' title='October 21st Practice Session'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_rZy4UM398G0/SuCeMtuYiLI/AAAAAAAAAAU/qPbq1k164JU/s72-c/pal_call_graph.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-993755517384231460</id><published>2009-10-18T09:16:00.000-07:00</published><updated>2009-10-18T09:37:33.404-07:00</updated><title type='text'>Alberta Collegiate Programming Competition</title><content type='html'>The two UVic teams, Vikes White and Blue, participated in the &lt;a href="http://www.cs.ualberta.ca/~contest/ACPC2009/contest/"&gt;Alberta Collegiate Programming Competition &lt;/a&gt;yesterday.  The Whites team consisted of Dan Sanders, John Hawthorn, and Scott Porter, while the Blues had students Tim Song, Tristin Sturgess, and Tyler Cadigan.  You can see the final &lt;a href="http://www.cs.ualberta.ca/~contest/ACPC2009/contest/scores.php"&gt;scoreboard here.&lt;/a&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It was a great competition and turn out, 37 teams participated.  Vikes White was the top performing UVic team, finishing 9th overall with 8 of 10 problems solved.  Vikes Blue finished 22nd overall with 3 problems solved.  Stanford took first place, UBC second, and Waterloo was third, all with 9 problems solved.  &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Nick Matthijssen, a graduate student in the CHISEL research lab at UVic, and I also formed an unofficial team and worked on the problems.  We solved 8 of 10 in about 4 hours and would have finished 5th or 6th.  &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Both teams had a good start, the Whites solved their first problem at 13 minutes while the Blues got one at 20 minutes.  Things slowed down for both teams for a while, and eventually both had 3 problems solved and were stuck with getting a 4th.  Eventually the Whites really stepped up the pace during the third and fourth hour, solving 4 problems in 70 minutes.  The  Blues got stuck with problem F, and were never able to figure out the cause of the wrong answers.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I was extremely proud of both teams.  Waterloo, UBC, and Stanford will most likely be some of the top teams at the world finals this coming year, so to lose by one problem to those teams is certainly acceptable.  It's good company to keep :-).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;One of the most amazing things about the Whites team is that they received zero penalties for wrong answers.  They were the only team that solved a significant amount problems to receive no penalties.  This kind of precision could be a huge asset at regionals next month if they can solve some problems a little faster.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Since this post is already quite lengthy, I'll save the problem analysis for a separate post.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-993755517384231460?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/993755517384231460/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/10/alberta-collegiate-programming.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/993755517384231460'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/993755517384231460'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/10/alberta-collegiate-programming.html' title='Alberta Collegiate Programming Competition'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-3778587904563655181</id><published>2009-10-14T11:30:00.000-07:00</published><updated>2009-10-16T08:39:06.883-07:00</updated><title type='text'>UVic Weekend Practice</title><content type='html'>Last Saturday, the two UVic teams participated in a practice session with teams from SFU, UBC, and Columbia university.  Columbia was first overall with 6 problems solved, and two individuals from UBC also solved 6 to claim second and third.&lt;br /&gt;&lt;br /&gt;We had a bit of a rough start as some UVic team members were locked out of the building and arrived late.  Also, the top UVic team was missing a member and the other team had one member that had never competed before.  The top UVic team solved 5 problems and finished 5th overall, just being edged out by SFU's top team, that also solved 5 problems.  The second UVic team solved 5 problems, but finished 10th overall.&lt;br /&gt;&lt;br /&gt;A lot of teams managed to solve 5 problems, as there was 5 reasonably easy and short to code problems, and then 3 hard problems.  Of the hard problems, all of the teams to solve 6 solved problem G from this problem set: http://cs.stanford.edu/group/acm/SLPC/problems.pdf&lt;br /&gt;&lt;br /&gt;Problem G is identical to problem F, except that there are far more rectangles.  You can solve F with a straight-forward N^2 implementation of the standard dynamic programming solution for longest increasing subsequence.  However, problem G requires an NlogN solution as there are as many as 100,000 rectangles.  There is a known NlogN solution for solving the longest increasing subsequence problem, however, none of the UVic students knew of this algorithm.  It may also be possible to implement enough short-cuts in the N^2 solution to drop it down under the time limit.&lt;br /&gt;&lt;br /&gt;The two unsolved problems were B and H.  B is a geometry problem, where you need to figure out how many balls can fill a fruit bowl given the description of the fruit bowl and a description for how it should be filled.&lt;br /&gt;&lt;br /&gt;In problem H, you have to find valid solutions to a 2x2x2 rubik cube.  You are given an initial configuration and you can apply 3 different spins a number of times to try to get all sides to have a single color.&lt;br /&gt;&lt;br /&gt;I'm not really sure how H can be solved within a standard 2 or 5 second time limit without breaking the standard memory constraints for a problem.  It's possible to solve the problem using a bounded breadth first search algorithm, with efficient implementations of the spins and storing seen states, but this is still slow without relying on significant amounts of memory.  It's also possible to pack the cube state into two integer values rather than a 6x8 char array to cut down on memory, but then extra processing time has to be spent on packing and unpacking the values for applying the spins.&lt;br /&gt;&lt;br /&gt;The teams are competing this weekend in the Alberta Collegiate Programming Competition.  There will be a lot of teams from Canada and the US competing, so it should give us a good chance to see how we will fair at regionals.&lt;br /&gt;&lt;br /&gt;Finally, I am speaking today at a computer science event for undergraduates to try to generate more interest in the ACM ICPC contest.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-3778587904563655181?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/3778587904563655181/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/10/uvic-weekend-practice.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/3778587904563655181'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/3778587904563655181'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/10/uvic-weekend-practice.html' title='UVic Weekend Practice'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-9177231507291540485</id><published>2009-10-08T09:05:00.000-07:00</published><updated>2009-10-08T09:19:18.891-07:00</updated><title type='text'>October 7th Practice Session</title><content type='html'>We had our first organized team practice session last night.  I had Dan, Scott, and John work on problems from the University of Waterloo's local contest from September 27th, 2009, http://plg1.cs.uwaterloo.ca/~acm00/090927. while Tristan and Tim worked on four different problems requiring bruteforce solutions.&lt;br /&gt;&lt;br /&gt;Dan, Scott, and John struggled a bit on the Waterloo problems.  This was partly due to initially using an incorrect approach to a reasonably easy problem.  The problem set consisted of 5 problems, two of which were quite straightforward, and two which were solvable but required careful implementation, and the 5th problem I am still unsure how to solve.&lt;br /&gt;&lt;br /&gt;With Tristan and Tim, after explaining different bruteforce approaches such as backtracking, enumerating permutations, and so forth, along with how to determine whether a problem can be bruteforced, they solved all four problems that I gave them.  The only problem they had was a time limit exceeded on their first problem, which was actually due to Java's slow Scanner object creation rather than an incorrect algorithm implementation.  They were able to get around the issue by modifying the way they read in their input.&lt;br /&gt;&lt;br /&gt;Both teams are competing in a practice competition this coming Saturday along with teams from SFU, UBC, and Columbia.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-9177231507291540485?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/9177231507291540485/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/10/october-7th-practice-session.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/9177231507291540485'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/9177231507291540485'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/10/october-7th-practice-session.html' title='October 7th Practice Session'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-2332368573260067352</id><published>2009-09-22T04:49:00.000-07:00</published><updated>2009-09-22T07:21:18.318-07:00</updated><title type='text'>ACM Teams Qualifier I</title><content type='html'>Simon Fraser University hosted an ACM Team Qualifier competition this past weekend for UBC, SFU, and UVic students.  SFU had 30 students compete, UBC had 21 and UVic had 6.  Dan Sanders from UVic finished third, while John Hawthorn, also from UVic, finished 13th.  The complete final standings are here: &lt;a href="http://www.cs.sfu.ca/news/events/ACM/090919/full.html"&gt;http://www.cs.sfu.ca/news/events/ACM/090919/full.html&lt;/a&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Dan jumped out to an early lead in the competition by solving problem A on the first try, 13 minutes into the competition.  Both Dan and John fell behind when they got stuck trying to solve a second problem.  Dan submitted D, but received a time limit exceeded response while John attempted B, but kept getting wrong answers.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Dan managed to catch up to the leaders and move into third in the last 30 minutes by solving both B and D.  He did not have time to make an attempt on problem C.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The problem set is available here: &lt;a href="http://www.cs.sfu.ca/news/events/ACM/"&gt;http://www.cs.sfu.ca/news/events/ACM/&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It was a pretty good set of problems.  Below I give my high-level analysis of each problem.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;A - The problem is to determine in what order to do your laundry based on the constraint that all laundry takes 30 minutes to wash, but varying amounts to dry.  You only have one washer and one dryer, so you can't dry more than one item at a time.  The key is to realize that the fastest way to do your laundry is always to handle the longest drying items first.  This is a greedy approach, requiring a simple sort of the laundry items.  The second part of the problem is to properly print the result.  Many wrong answers were a result of improper digital clock formatting.  This problem was the easiest problem in the set.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;B - In this problem, you have to determine which players can reach the final of a tennis tournament.  There is a maximum of 16 players, and you know which players will win when a given player faces off against another player.  The key insight is to realize that since there are only 16 players, you can try all combinations, generating all possible tournament situations.  This can be done in a number of ways, recursively is probably the simplest way.  However, only four students managed to solve this problem.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;C - I actually thought this was the second easiest problem to solve, but during the competition, it was the third most solved problem and no UVic student attempted it.  The problem describes two languages and four rules for translating a word from one language to the other.  When a word is translated from the first language to the second, and then back to the first, it's possible to generate a new word, that is a synonym of the original word.  To solve the problem, you have to find all possible synonyms and print them in alphabetical order.  The catch is that you may have to apply the translation rules multiple times in order to discover all the unique synonyms.  I think perhaps this was not completely clear in the problem text, thus, some students were not sure how to solve the problem.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;To actually solve the problem, the algorithm simply needs to apply the four rules, then apply the reverse rules, saving the list of synonyms.  Continue this process on the newly generated list until no new synonyms can be created.  Then print the complete list of words.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;D - This problem is a classic computer science problem.  You are given a large list of points in 2D space, and you need to find the line that contains the most points from the list.  This was the second least solved problem, due to the number of points being as many as 400, thus a reasonably efficient solution was necessary.  There are many ways to address this problem.  One approach could be to calculate slopes and y-intersects between all points (N^2).  Then find the points that share the most slopes and y-intersects.  An alternative approach could be to calculate the angles between points x and y for all x and y, then count the number of points that have the same relative angle to x.  To get this solution under the time limit, your code had to be reasonably efficient.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;E - This was the second most solved problem.  The problem describes how the Canucks hockey team has a certain probability of winning a game against another team.  However, when they win, in the next game, they have a higher probability of winning, but if they lose, then they have a lower probability of winning the next game.  Given values for this change in probability, you need to calculate the probability that the Canucks will win a best of 7 game hockey series.  To solve this, all you need to do is bruteforce all the possible win and loss scenarios (there's not that many), summing the probabilities for when the Canucks win the series.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;There is another competition coming up this weekend, but as I am out of the country right now, UVic may have to wait until Wednesday to attempt the problem set.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-2332368573260067352?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/2332368573260067352/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/09/acm-teams-qualifier-i.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/2332368573260067352'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/2332368573260067352'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/09/acm-teams-qualifier-i.html' title='ACM Teams Qualifier I'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-2248646948010481801</id><published>2009-09-17T14:03:00.000-07:00</published><updated>2009-10-14T11:55:38.282-07:00</updated><title type='text'>New ACM ICPC Season</title><content type='html'>&lt;span class="Apple-style-span"  style="font-size:small;"&gt;It's been quite a while since I posted, but the Pacific Northwest regional competition is coming up in about a month. As a result, beginning last night, I've started training UVic undergrads in preparation for the competition.  I had all the newbies work on four different simple problems, mostly dealing with string processing.&lt;/span&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;Links to problems:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="border-collapse: collapse; white-space: pre-wrap;font-family:arial;" &gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;http://uva.onlinejudge.org/index.php?option=com_onlinejudge&amp;amp;Itemid=8&amp;amp;category=96&amp;amp;page=show_problem&amp;amp;problem=585&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="border-collapse: collapse; white-space: pre-wrap;font-family:arial;" &gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;http://uva.onlinejudge.org/index.php?option=com_onlinejudge&amp;amp;Itemid=8&amp;amp;category=96&amp;amp;page=show_problem&amp;amp;problem=1056&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="border-collapse: collapse; white-space: pre-wrap;font-family:arial;" &gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;http://uva.onlinejudge.org/index.php?option=com_onlinejudge&amp;amp;Itemid=8&amp;amp;category=96&amp;amp;page=show_problem&amp;amp;problem=951&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span class="Apple-style-span" style="border-collapse: collapse; white-space: pre-wrap;font-family:arial;" &gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;http://uva.onlinejudge.org/index.php?option=com_onlinejudge&amp;amp;Item&lt;/span&gt;&lt;/span&gt;&lt;span class="Apple-style-span" style="border-collapse: collapse; white-space: pre-wrap;font-family:arial;" &gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;id=8&amp;amp;category=96&amp;amp;page=show_problem&amp;amp;problem=350&lt;/span&gt;&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="border-collapse: collapse; white-space: pre-wrap;"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;Everyone solved at least one problem and several solved a couple over the 3 hour meeting time.  I had the more experienced members work as a team on problems from the 2002 Rocky Mountain regional.  Working in a team of two, they solved 5 problems in about 2.5 hours.  The problems in this particular regional were pretty straightforward, but it's still an encouraging start.  &lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="border-collapse: collapse; white-space: pre-wrap;"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="border-collapse: collapse; white-space: pre-wrap;"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;I'm becoming more and more convinced that Java is the better competition language for the ACM competitions, especially if you have access to Eclipse.  With Java 1.6, the combination of the various collections, scanner class, and regular expressions makes it quite attractive over C++.  &lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="border-collapse: collapse; white-space: pre-wrap;"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="border-collapse: collapse; white-space: pre-wrap;"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;For example, one of the problems from the Rocky Mountain regional was to parse e-mail addresses out of a stream of ASCII text.  Back in my competition days, using C++, I would have had to painstakingly encoded the various matching rules to parse the strings looking for the e-mail addresses.  However, last night, one of the students quickly solved this problem with less than 10 lines of code using a regular expression for the matching in Java.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="border-collapse: collapse; white-space: pre-wrap;"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="border-collapse: collapse; white-space: pre-wrap;"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;What in C++ would be an ugly set of  methods, becomes the following regular expression in Java:&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="border-collapse: collapse; white-space: pre-wrap;"&gt;&lt;pre name="code" class="Cpp"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;Pattern.compile("[A-Za-z0-9_-]&lt;br /&gt; (\\.?[A-Za-z0-9_-])*@[A-Za-z0-9_-](\\.?[A-Za-z0-9_-])*");&lt;/span&gt;&lt;/pre&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="border-collapse: collapse; white-space: pre-wrap;"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="border-collapse: collapse; white-space: pre-wrap;"&gt;&lt;span class="Apple-style-span"  style="font-size:small;"&gt;Also, using an IDE like Eclipse removes much of the tedium of dealing with some of Java's rather verbose syntax.  I've seen experienced Eclipse users rapidly produce code through a combination of typing commands and hitting CTRL+space for term completion.  Eclipse will also automatically compile, fix your imports, point out syntax errors, and has an integrated interactive debugger.&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-2248646948010481801?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/2248646948010481801/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/09/new-acm-icpc-season.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/2248646948010481801'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/2248646948010481801'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/09/new-acm-icpc-season.html' title='New ACM ICPC Season'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-4755786000627995879</id><published>2009-05-05T09:53:00.000-07:00</published><updated>2009-05-05T10:43:14.749-07:00</updated><title type='text'>SRM 439 Div 2</title><content type='html'>I competed on &lt;a href="http://www.topcoder.com/"&gt;TopCoder&lt;/a&gt; last week.  I submitted solutions to all three problems, but due to some pretty silly mistakes, only my 500 solution passed the test cases.  Pretty shameful :-(.&lt;br /&gt;&lt;br /&gt;The problem set was actually quite do-able.  The first problem was, given a rectangular grid of integers, find the largest square that contains the same digit in all four corners. My solution was to loop through each element (i,j) in the grid, then through all elements to the right of the given element.  If any element to the right matched the given cell (i,j), then I would check to see if the cell located in the bottom left and bottom right hand corners of the square also have the same number.  If so, store the size of the square if it is larger than my current best.&lt;br /&gt;&lt;pre name="code" class="Cpp"&gt;int getMax(vector &lt;string&gt; data) {&lt;br /&gt; int ans = 1;&lt;br /&gt; for(int i = 0; i &amp;lt; data.size(); i++) {&lt;br /&gt;  for(int j = 0; j &amp;lt; data[i].size(); j++) {&lt;br /&gt;     int v = data[i][j]-'0';&lt;br /&gt;     for(int k = j+1; k &amp;lt; data[i].size(); k++) {&lt;br /&gt;       if(data[i][k]-'0' == v) {&lt;br /&gt;         // length of square&lt;br /&gt;         int d = k - j;&lt;br /&gt;         if(i + d &amp;lt; data.size() &amp;amp;&amp;amp; data[i+d][j]-'0' == v&lt;br /&gt;            &amp;amp;&amp;amp; data[i+d][k]-'0' == v)&lt;br /&gt;           ans = max(ans, (d+1)*(d+1));&lt;br /&gt;       }&lt;br /&gt;     }&lt;br /&gt;   }&lt;br /&gt; }&lt;br /&gt; return ans;&lt;br /&gt;}&lt;br /&gt;&lt;/string&gt;&lt;/pre&gt;Unfortunately, I made a really silly mistake with my indices during the contest by accidentally checking if data[i+d][k] matched v twice rather than data[i+d][j].&lt;br /&gt;&lt;br /&gt;In the second problem, you have N bottles, each with unlimited capacity, initially all containing 1 liter of water.  You can pour one bottle into another provided the amount of water currently in the two containers is equal.  You need to reduce the number of bottles to at most K.  If it's not possible to do that with exactly N bottles, you can buy additional bottles.  You have to return the minimum number of additional bottles that you must purchase.&lt;br /&gt;&lt;br /&gt;This can be solved quite easily once you make a simple observation. First, given a number M, you want to determine how many bottles will be left over after you reduce the number of bottles as much as possible (that is, all possible pourings).  Taking an example of M = 13, then initially you have:&lt;br /&gt;&lt;br /&gt;1 1 1 1 1 1 1 1 1 1 1 1 1&lt;br /&gt;After one round of pourings, you get:&lt;br /&gt;2 2 2 2 2 2 1&lt;br /&gt;Another round:&lt;br /&gt;4 4 4 1&lt;br /&gt;Last round:&lt;br /&gt;8 4 1&lt;br /&gt;&lt;br /&gt;So, you are left with 3 bottles after all possible pourings.  This process of reducing bottles to the minimum number is very similar to the process of converting a decimal number into binary.  To see this, let's take the same example again but convert M=13 into binary.&lt;br /&gt;&lt;br /&gt;13 = 6 * 2 + 1&lt;br /&gt;6 = 3 * 2 + 0&lt;br /&gt;3 = 1 * 2 + 1&lt;br /&gt;1 = 0 * 2 + 1&lt;br /&gt;13 in binary is 1011.&lt;br /&gt;&lt;br /&gt;Notice how the multiplier equals the number of bottles that perfectly pour into each other each round and the remainder is the bottle left over.  Also, interestingly, the number of 1s in the binary representation is equal to the number of bottles after all possible pourings!  Thus, to solve the problem, we can start at N, keep adding bottles and count the number of 1s in the binary representation and see if that number is less than or equal to K.&lt;br /&gt;&lt;pre name="code" class="Cpp"&gt;int getMinBottles(int N, int K) {&lt;br /&gt;int cur = N;&lt;br /&gt;while(__builtin_popcount(cur) &gt; K) cur++;&lt;br /&gt;return cur - N;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;In the last problem, you are given a string s, and you must determine the minimum number of insertions, deletions, and changes you would need to do in order to convert s into a palindrome.  There was also one other twist, you were also allowed to perform one swap operation.&lt;br /&gt;&lt;br /&gt;The problem is actually pretty easy to solve if you are familiar with the standard &lt;a href="http://en.wikipedia.org/wiki/Edit_distance"&gt;edit distance dynamic programming solution&lt;/a&gt;.  With edit distance, you want to know the minimum number of insertions, deletions, and changes to convert string s1 into string s2.&lt;br /&gt;&lt;br /&gt;Ignoring the swap operation, we can intepret this problem the same way by thinking of our string s in two halves.  Since we want s to be a palindrome, that means the substring for the first half of s needs to eventually equal the substring of the second half of s.  Thus, still ignoring the swap operation, we can easily compute the minimum number of operations to convert the first half into the second using the standard DP solution for edit distance.&lt;br /&gt;&lt;br /&gt;Ok, so we're almost there.  Now, we need to figure out what to do with the swap operation.  Well, the solution is actually pretty simple.  Since we can only do the swap once, we can just try performing all possible swap operations.  After each swap, check the edit distance calculation and store the minimum number of operations plus an additional one for the swap.  The algorithm would be similar to the following:&lt;br /&gt;&lt;pre name="code" class="Cpp"&gt;int getEditDistance(string initial) {&lt;br /&gt; // compute distance from the two halves&lt;br /&gt; int ans = editDistance(initial);&lt;br /&gt; // try all swaps and recalculate distance&lt;br /&gt; for(int i = 0; i &amp;lt; initial.size); i++) { &lt;br /&gt;   for(int j = i+1; j &amp;lt; initial.size(); j++) {   &lt;br /&gt;      swap(initial[i], initial[j]);   &lt;br /&gt;      ans = min(ans, 1+editDistance(initial));   &lt;br /&gt;      swap(initial[i], initial[j]); &lt;br /&gt;   }&lt;br /&gt; }&lt;br /&gt; return ans;&lt;br /&gt;} &lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-4755786000627995879?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/4755786000627995879/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/05/srm-439-div-2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/4755786000627995879'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/4755786000627995879'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/05/srm-439-div-2.html' title='SRM 439 Div 2'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-1904419414547183497</id><published>2009-04-27T09:02:00.000-07:00</published><updated>2009-04-27T09:28:21.897-07:00</updated><title type='text'>ACM ICPC Finals Problem Set</title><content type='html'>I only read through the problems once and didn't spend too much time thinking about them, but here's my analysis based on my first impression.  First, check out the &lt;a href="http://cm2prod.baylor.edu/resources/pdf/2009Problems.pdf"&gt;problem set here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;A. This problem looks reasonably straightforward.  Most teams solved it.  There's only 8 planes, so you can try all 8! orderings and use binary search to compute the minimum time.  After that, place all planes as close together as possible and keep track of the best answer.&lt;br /&gt;&lt;br /&gt;B. I don't think there's anything particularly tricky about this problem.  I believe you just implement the gates and simulate the execution.  In the situation where you need to track down the bad gate, you can try making each gate bad and see which one results in correct answers.  There's only 19 gates, so it should be pretty do-able.&lt;br /&gt;&lt;br /&gt;C. This problem is very similar to a classic problem with an ant or spider on a cube.  I actually received this problem during a job interview once.  With the cube, you unfold the cube in every possible way and then compute the shortest distance in 2D.  For an octahedron, the same process should work.  It's probably a bit tricky to get all the details correct.&lt;br /&gt;&lt;br /&gt;D. Only idea I have is to try all possible configurations.&lt;br /&gt;&lt;br /&gt;E. Haven't solved it yet.&lt;br /&gt;&lt;br /&gt;F. Doesn't look too bad and a lot of teams solved it.  I think you can compute the convex hull between points and then extend that based on the margin.  You can combine this perimeter with rounded segments based on circles enclosing the points.  To figure out the smallest fence, use dynamic programming (DP) over the point subsets and fence lengths.&lt;br /&gt;&lt;br /&gt;G. This is a two player game problem, but I think you can bruteforce it.  You have to carefully implement the various rules of the game, but the number of possible states is reasonably small provided you cut branches that are bad.&lt;br /&gt;&lt;br /&gt;H. This problem can be formulated as 2-SAT, which has a well-known solution using depth-first-search.  To determine the result for a variable, you can fix the answer and run 2-SAT, checking if the formula is still satisified.&lt;br /&gt;&lt;br /&gt;I. I need to read this problem again, but it looks like a simulation problem.&lt;br /&gt;&lt;br /&gt;J. Not sure of the details yet.  Looks like DP.&lt;br /&gt;&lt;br /&gt;K. Looks like DP mixed with shortest-path.  Haven't worked out the details.&lt;br /&gt;&lt;br /&gt;Looks like a good set of problems.  There was a decent spread of the teams, with most solving at least one or two, and no team solving them all.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-1904419414547183497?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/1904419414547183497/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/04/acm-icpc-finals-problem-set.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/1904419414547183497'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/1904419414547183497'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/04/acm-icpc-finals-problem-set.html' title='ACM ICPC Finals Problem Set'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-7980868528676981953</id><published>2009-04-24T08:19:00.000-07:00</published><updated>2009-04-24T08:37:15.397-07:00</updated><title type='text'>ACM ICPC World Finals</title><content type='html'>The &lt;a href="http://cm2prod.baylor.edu/login.jsf"&gt;ACM ICPC World Programming Finals&lt;/a&gt; took place a few days ago on April 21st.  St. Petersburg State University of IT, Mechanics and Optics was first, Tsinghua University second, and St. Petersburg State University was third.  You can check out the full standings &lt;a href="http://cm2prod.baylor.edu/ICPCWiki/Wiki.jsp?page=Results%20World%20Finals%202009"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;As usual, Waterloo was Canada's top competitor, finishing in the medals for the 6th year in a row with six problems solved.  I am always amazed at how well Waterloo does.  They are consistently in the medals regardless of their regional performance.  At their regional this year, they finished 3rd behind two teams from Carnegie Mellon University, yet at the finals, they beat Carnegie Mellon.  Of course, it's easy to have a bad competition or get stuck on a problem, but they always seem to bring their A-game at the finals.&lt;br /&gt;&lt;br /&gt;UBC was the second highest Canadian team, finishing tied for 34th with four problems solved.  The other Canadian teams (Alberta and McGill) both received honourable mention. This is not the strongest showing for Canada, but a number of typically good teams did not place (e.g. CalTech).&lt;br /&gt;&lt;br /&gt;Russia and China dominated the medal positions.  North America only claimed three spots out of 13, with MIT being the top North American team finishing 7th with seven problems solved.  One surprising medal winner was &lt;a href="http://www.tsu.ge/indexe.asp"&gt;Tbilisi State University&lt;/a&gt; from Georgia.  At least, I cannot recall them ever being at the finals let alone in the medals.  Of course, I could be wrong.&lt;br /&gt;&lt;br /&gt;Stanford University was the top placing team from UVic's region, finishing 20th.  UBC was next and then Berkeley.  UBC was actually beat by Berkeley in the regional competition, so it's nice to see them get some kind of revenge.&lt;br /&gt;&lt;br /&gt;I haven't read the problems yet, but I'll probably check them out soon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-7980868528676981953?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/7980868528676981953/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/04/acm-icpc-world-finals.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/7980868528676981953'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/7980868528676981953'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/04/acm-icpc-world-finals.html' title='ACM ICPC World Finals'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-8124400716120795208</id><published>2009-04-09T08:40:00.000-07:00</published><updated>2009-04-09T08:57:10.230-07:00</updated><title type='text'>Regionals date</title><content type='html'>I am finally back from my almost month long vacation to Japan and China.  The trip was fantastic.  It was nice to take a break from programming and work for such a long period, but now I am back and trying to scramble to get caught up.  I am teaching a third year course on HCI this summer, so I need to prepare for that.  I am also starting as a postdoc in May and doing that until the end of the year.&lt;br /&gt;&lt;br /&gt;While I was away, I received an e-mail from the Pacific Northwest Regional director.  Apparently, the world programming finals for the ACM ICPC is happening a month earlier than usual next year, so regionals need to be complete by November 1st, 2009.  Normally, the Pacific Northwest Regional takes place around the 20th of November, but this coming Fall it will be on the 31st of October.  That means I'll have almost 3 weeks less time to prepare my teams.&lt;br /&gt;&lt;br /&gt;Luckily, the programming club from this past term was pretty successful, so I should have some returning programmers for next year.  I am hoping to take two teams this year and I think the top team should be reasonably strong.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-8124400716120795208?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/8124400716120795208/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/04/regionals-date.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/8124400716120795208'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/8124400716120795208'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/04/regionals-date.html' title='Regionals date'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-2504765749473660038</id><published>2009-03-12T17:05:00.000-07:00</published><updated>2009-03-12T17:09:09.413-07:00</updated><title type='text'>PhD Defense</title><content type='html'>This is not a very programming related post, but I had my PhD defense today.  It went very well.  My thesis is being nominated for the University of Victoria Gold Medal, which is the prize for the top PhD thesis at the university.  It is probably a long shot as I'm sure there are plenty of good theses out there.  Also, I don't know how any committee could compare a PhD from math with a PhD from philosophy and one from computer science.  However, it is still nice to be nominated.&lt;br /&gt;&lt;br /&gt;I am off to Japan tomorrow morning and on the 23rd I am heading to China so I won't be competing on Top Coder until some time in April.  Hopefully I'll get in a little practice while I am away.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-2504765749473660038?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/2504765749473660038/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/03/phd-defense.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/2504765749473660038'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/2504765749473660038'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/03/phd-defense.html' title='PhD Defense'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-146717376267108664</id><published>2009-03-10T08:45:00.000-07:00</published><updated>2009-03-10T09:41:49.799-07:00</updated><title type='text'>TCO09 Round 1</title><content type='html'>I competed in the Top Coder Open 2009 Round 1 last Saturday after qualifying the previous week.  My friends Nick and Oleg also competed.  You had to finish in the top 720 to qualify for the next round, but only Oleg managed to do that out of the three of us.  I was very close, but unfortunately an off by one error caused me to fail system tests.  &lt;br /&gt;&lt;br /&gt;The first problem was pretty easy.  You were given a number N between 1 and 10^10 and another number L between 2 and 100 and you wanted to find a sequence of numbers of length at least L that summed to N.  &lt;br /&gt;&lt;br /&gt;I realized that candidate numbers would be close to N/L for L up to 100.  So my algorithm tries all numbers and their sums within those ranges keeping track of the best one.  &lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;vector&amp;lt;int&amp;gt; sequence(int N, int L) {&lt;br /&gt;   if(L &gt; 100) return vector&lt;int&gt;(0);&lt;br /&gt;   vector&amp;lt;int&amp;gt; r, best;&lt;br /&gt;&lt;br /&gt;   for(int j = L; j &lt;= 100 ; j++) {&lt;br /&gt;      int mid = N / j;&lt;br /&gt;      // define an arbitrary range of numbers to try&lt;br /&gt;      int start = max(mid - 1000, 0);&lt;br /&gt;      int end = mid + 1000;&lt;br /&gt;&lt;br /&gt;      // try all numbers&lt;br /&gt;      for(int i = start; i &amp;lt;= end; i++) {&lt;br /&gt;         int sum = 0;&lt;br /&gt;         for(int k = i; k &lt;= N; k++) {&lt;br /&gt;           sum += k;&lt;br /&gt;           r.push_back(k);&lt;br /&gt;           if(sum &amp;gt; N) break;&lt;br /&gt;           if(sum == N &amp;&amp; r.size() &amp;gt;= L &lt;br /&gt;                 &amp;&amp; r.size() &amp;lt;= 100 &amp;&amp; (r.size() &lt; best.size() &lt;br /&gt;                 || (!best.size() &amp;&amp; r.size() &amp;lt;= 100)) {&lt;br /&gt;             best = r;&lt;br /&gt;           }&lt;br /&gt;         }&lt;br /&gt;         r.clear();&lt;br /&gt;      }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    return best;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;I got the code implement pretty quickly and submitted for a pretty decent score. Provided I passed the system tests, with the one problem solved I would have been close to qualifying for the next round.  Unfortunately, when I submitted my loop from k=i to k &amp;lt;= N was written as k=i to k &amp;lt; N.  That simple mistake cost me the competition :-).  There are nicer ways to solve this problem using the sum formula, but I still like my semi-bruteforce approach.&lt;br /&gt;&lt;br /&gt;I got stuck on the 500 problem and glanced quickly at the 1000 out of desperation.  I should have moved onto the 1000 point problem because I think it was easier than the 500 even though it required a lot more code.  I solved  the 1000 point problem yesterday.  &lt;br /&gt;&lt;br /&gt;In that problem, you are given the description of a new chess piece called a "Unicorn".  It moves by choosing any of the 4 basic directions and moving more than 2 squares in that direction, then turning orthogonally in one of the two possible directions and moving more than 1 square in that direction.  You are given a randomly generated chessboard of size up to 300x300 where each square or cell has a letter (A through Z).  You are also given a word that can be spelled out on the chessboard by moving a unicorn piece.  You have to determine how many possible ways you can generate the given word by moving a unicorn piece around the chessboard.&lt;br /&gt;&lt;br /&gt;For example, consider the chessboard below:&lt;pre&gt;&lt;br /&gt;ABCD&lt;br /&gt;AAAA&lt;br /&gt;AABC&lt;br /&gt;AAAB&lt;br /&gt;&lt;/pre&gt;If we want to construct the word "AB", there's only two ways to do this.  We place our unicorn piece in the top left-hand corner getting the "A".  We can then move our chess piece 3 places to the bottom left-hand corner, turn orthogonally to the right, and move 3 places to the right to get a "B".  We can also reach the bottom right-hand "B" by placing our unicorn in cell (1,0).&lt;br /&gt;&lt;br /&gt;The key to solving this problem is to realize that assuming you arrived in a cell, there's a fairly small number of cells that you could not possibly have been at previously to reach this cell.  You can keep track of the number of paths to reach a given cell by counting the number of ways to reach all previous letters and subtracting off the cells that you could not possibly have come from.&lt;br /&gt;&lt;br /&gt;Moving back to our example from before, initially we can get to any cell with an A in one step.  Meaning that you can travel to that cell one way, essentially place the unicorn directly on it.  Represented as a matrix it looks like this:&lt;pre&gt;&lt;br /&gt;1000&lt;br /&gt;1111&lt;br /&gt;1100&lt;br /&gt;1110&lt;br /&gt;&lt;/pre&gt;  Now we can consider any cells with a B.  The first is cell (0,1).  There's a total of 10 possible A's that we could have used to arrive at this B.  We can cross off all the cells that we could not possibly have jumped from to reach this location. This includes B's row, column, previous column, next column, previous row, next row, and a few others.  &lt;pre&gt;&lt;br /&gt;XBXX&lt;br /&gt;XXXX&lt;br /&gt;XXXX&lt;br /&gt;XXXB&lt;br /&gt;&lt;/pre&gt; We can count all the A's that were crossed off, which turns out to be 10 and remove this from the total number of A's, which is also 10.  This means there are zero ways that this particular cell could be the second position in a valid path to form the word "AB".  We can continue doing this.  Once we reach cell (3,3), the crossed off matrix looks like this:&lt;pre&gt;&lt;br /&gt;AXXX&lt;br /&gt;AXXX&lt;br /&gt;XXXX&lt;br /&gt;XXXB&lt;br /&gt;&lt;/pre&gt;There were 8 "A"s crossed off, so there are 2 ways to get to this "B".&lt;br /&gt;&lt;br /&gt;For longer words, the process is much the same.  There's a few catches in the problem, like making sure you handle large sums appropriately as you can potentially get a very large number of paths.  Also, there's a small trick to get the algorithm to run in O(N^2) rather than (N^3).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-146717376267108664?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/146717376267108664/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/03/tco09-round-1.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/146717376267108664'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/146717376267108664'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/03/tco09-round-1.html' title='TCO09 Round 1'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-2134208509208974402</id><published>2009-03-04T08:13:00.000-08:00</published><updated>2009-03-04T08:46:27.274-08:00</updated><title type='text'>TopCoder Practice - SRM 254</title><content type='html'>I participated in a TopCoder practice session yesterday with &lt;a href="http://publish.uwo.ca/~ogolubit/"&gt;Oleg&lt;/a&gt; and a new student in our research group, Nick.  It turned out to be my best SRM in quite some time.  I think I submitted all three problems before Oleg and Nick had submitted their second solution.  I was a little worried that I had messed something up since I finished so quickly.  &lt;br /&gt;&lt;br /&gt;I started looking at my 500 and 1000 point solutions to see if I missed something in the problem.  I was pretty sure my 500 solution was correct.  The problem involved string matching.  You were given a string along with a potentially short-hand version of that string.  For example, the string "capture" can be shortened as "cptr".  You had to return the letters that could be removed from the first string such that the remaining letters matched the shortened version.  In the previous example, the answer would be "aue".  If no match is possible, return "UNMATCHED".  I scored 481.63 points on this problem, which is probably my best score on a 500 point problem.  &lt;br /&gt;&lt;br /&gt;The solution is pretty straight-forward.  The max length of the string is 50 characters, so you can use an N^2 string matching algorithm.  I looped through the shortened version of the string, and had an inner loop that looked for matches on the larger string.  On the first match, I break from the inner loop and add all the characters before the match to the string I will return.  If I didn't find a match then I know I can't make this string.&lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;// typed is the shorter string, phrase is the original&lt;br /&gt;string probableMatch(string typed, string phrase) {&lt;br /&gt;  string s = "";&lt;br /&gt;  int last = 0;&lt;br /&gt;  for(int i = 0; i &amp;lt; typed.size(); i++) {&lt;br /&gt;     int j = last;&lt;br /&gt;     for(; j &amp;lt; phrase.size(); j++) if(typed[i] == phrase[j]) break;&lt;br /&gt;&lt;br /&gt;     // we found a match, so add the suffix to s&lt;br /&gt;     if(j != phrase.size()) {&lt;br /&gt;        s += phrase.substr(last, j - last);&lt;br /&gt;        last = j+1;&lt;br /&gt;     }&lt;br /&gt;     else return "UNMATCHED";&lt;br /&gt;  }&lt;br /&gt;  s += phrase.substr(last, phrase.size() - last);&lt;br /&gt;&lt;br /&gt;  return s;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The 1000 point problem was a really fun problem.  You were given a description about how pigs choose a trough to eat from.  The piglets attempt to choose a trough that will delay the time when they become sandwiched in by two piglets.  After reading the problem, I realized based on the description that you could solve it using a greedy strategy.  Each choice made by a piglet is a locally optimal decision, so you just need to process the choices in the correct order and return the first one that is available.  Although I got quite a few points for the problem, I could have done it much faster by using C++'s built in string find rather than writing loops to do all the look ups.  Both Nick and Oleg used dynamic programming for the problem, not realizing that greedy works.&lt;br /&gt;&lt;br /&gt;It turned out that I was right about missing something in the problems, but it wasn't on the 500 and 1000 point problems, it was with the 250.  I actually failed the system tests on the 250 problem.  It's only the second time I've had that happen.  I made a pretty silly mistake and didn't make use of a formula given in the problem.  Instead I tried to figure it out with my own formula.  &lt;br /&gt;&lt;br /&gt;Despite this error, I still would have been second overall in the SRM and I beat both Oleg and Nick during the practice.  Tonight Nick and I are participating in the 3rd qualification round for the &lt;a href="http://www.topcoder.com/tco09"&gt;Top Coder Open&lt;/a&gt;.  Hopefully we'll qualify. Oleg has already qualified.  The first official round is this Saturday.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-2134208509208974402?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/2134208509208974402/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/03/topcoder-practice-srm-254.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/2134208509208974402'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/2134208509208974402'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/03/topcoder-practice-srm-254.html' title='TopCoder Practice - SRM 254'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-7906734354417990055</id><published>2009-02-26T09:39:00.000-08:00</published><updated>2009-02-26T12:00:37.095-08:00</updated><title type='text'>Huh?  No unsigned values in Java?</title><content type='html'>I've been working on this program with my friends &lt;a href="http://www.iqc.ca/~dmaslov/"&gt;Dmitri&lt;/a&gt; and &lt;a href="http://publish.uwo.ca/~ogolubit/"&gt;Oleg&lt;/a&gt;. The program computes permutations of reversible circuits (i.e. NOT gates, CNOT gates, etc.) for different numbers of inputs.  The gates are simulated by performing bit operations on 64-bit unsigned integers. The details aren't that important, but what's interesting is that the number of permutations gets massively huge as the number of inputs increases.  To store results for 8 inputs, you need an array of size (2^29)*8, where the 8 is for 64-bit unsigned integers.  &lt;br /&gt;&lt;br /&gt;Oleg developed a C++ version to do the calculations and after some tweaks and bug fixes we managed to get it to calculate up to 6 inputs in about 5 seconds.  With 7 inputs, things get a little tricky because you need a table of size (2^26)*8, but we also use three other arrays of similar size.  On our 2GB machines, this is too much.  However, it's possible to calculate for 7 inputs if we do something hacky like make one special table that size and leave the other ones smaller at size (2^23)*8.&lt;br /&gt;&lt;br /&gt;We want to calculate values for at least 8 inputs.  This is beyond our 2GB machines, at least if we keep everything in memory.  I originally worked on dumping our hashtables to files and merge sorting the files in the process when the hashtables get too large, but this is pretty slow.  After this I decided to try dumping the tables into a database.  I converted the program from C++ to Java and hooked in a MySQL database.&lt;br /&gt;&lt;br /&gt;This is where I ran into the issue that Java does not support unsigned long values.  I hadn't realized this, but of course, how often do you need a full 64-bit integer?  Luckily, I was able to get around the lack of unsigned values by first realizing that bit shift operators in Java do not care about the sign bit except for the right-shift (&gt;&gt;).  However, there's an unsigned version (i.e. &gt;&gt;&gt;).  The second problem was that you can't use less than and greater than operators because if you're using the sign bit, then a potentially large number will be negative.  To get around this, I wrote my own comparison function, which is shown below.&lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;/**&lt;br /&gt;* Bitwise comparison of longs.  This is to handle the fact &lt;br /&gt;* that Java does not support unsigned longs.&lt;br /&gt;*/&lt;br /&gt;static int compareLongs(long p, long q) {&lt;br /&gt;  if(p == q) return 0;&lt;br /&gt; &lt;br /&gt;  for(int i = 64; i &amp;gt;= 0; --i) {&lt;br /&gt;     long t1 = (1 &amp;lt;&amp;lt; i) &amp; p;&lt;br /&gt;     long t2 = (1 &amp;lt;&amp;lt i) &amp; q;&lt;br /&gt;     if(t1 &amp;lt; t2) return -1;&lt;br /&gt;     else if(t1 &amp;gt; t2) return 1;&lt;br /&gt;  }&lt;br /&gt;  return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I had to make some other tweaks as well, like writing a clone function for our hash table as assignment in Java works differently than C++.  Once I completed this, and the various bit shift changes, the Java version ran almost as fast as the C++ version.  &lt;br /&gt;&lt;br /&gt;For the database integration, I ended up doing a lot of MySQL configuration tweaks in order to get my bulk insertion speed faster.  I also discovered some new MySQL specific SQL syntax.  For example, you can use REPLACE INTO instead of INSERT INTO if you are going to potentially insert a record with a matching primary key.  The replace version will delete the old version and insert the new one.  I actually ended up using a similar SQL statement, INSERT IGNORE INTO, which does the same thing but does not delete the original record but simply ignores the new one.&lt;br /&gt;&lt;br /&gt;I'm not sure how useful this Java version is going to be as I managed to get access to a server with 8GBs of RAM where I can allocate enough memory for the C++ version to work. Also, Oleg was able to calculate inputs of 8 values by dumping multiple files to disk and then using the Unix sort program to merge and sort the files.  However, it was still a good learning experience and we can possibly use it for verifying our numbers.  I'll post a bit about the C++ version next time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-7906734354417990055?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/7906734354417990055/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/02/huh-no-unsigned-values-in-java.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/7906734354417990055'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/7906734354417990055'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/02/huh-no-unsigned-values-in-java.html' title='Huh?  No unsigned values in Java?'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-6386851103303835199</id><published>2009-02-20T09:06:00.000-08:00</published><updated>2009-02-24T18:56:39.173-08:00</updated><title type='text'>I'm an idiot</title><content type='html'>Last post I mentioned that I had not been able to come up with a decent algorithm to problem H from the Waterloo practice.  Well, yesterday, while running, I figured it out.  There's an N^2 algorithm that's pretty straight-forward.  &lt;br /&gt;&lt;br /&gt;The problem involves a bunch of planets and each planet has a certain political situation, 0 or 1.  A planet is considered unstable if given some value R, there are more planets within R parsecs of the given planet with the opposite political stance than the with the same political stance.  Your job is to find the minimum R such that the number of unstable planets is maximized.&lt;br /&gt;&lt;br /&gt;The obvious bruteforce solution is to consider all distances between planets, and for each planet, compute whether it is unstable or not.  However, this obvious solution is N^4 where N can be up to 1000.  That's a big big number.  &lt;br /&gt;&lt;br /&gt;There's a slightly better algorithm than this, where you can compute and store all distances.  Also, for every planet, store in one list the distance of all planets with the same political situation as you in sorted order and in another list the distance of all planets with the opposite political situation as you in sorted order.  Then, when you loop over all possible distances and all planets, you can use binary search on the sorted lists to figure out how many planets are for and against you at the given distance.  You can also do early outs when you can't possibly improve on the best score.  If you only store unique distances, then there will be less than N^2 of these, but could be as many as N^2/2.  Regardless of the optimizations, this algorithm is still way too slow, and has a big-Oh of O(N^3 log N).&lt;br /&gt;&lt;br /&gt;The N^2 algorithm that I came up with involves computing all distances between planets and storing these in sorted order along with the indices of the planets used to make that given distance.  Then, you can loop over all distances and use your previously calculated stabilization results to help solve for the current distance.  Since the distances are sorted, results computed for smaller distances can be combined to help compute values for the current distance.  &lt;br /&gt;&lt;br /&gt;In C++, this could look something like this:&lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;  int destab = 0;&lt;br /&gt;  // m is a multimap where the key is the distance &lt;br /&gt;  // from planets with indices stored in the  pair values&lt;br /&gt;  multimap&amp;lt;double, pair&amp;lt;int, int&amp;gt; &amp;gt;::iterator iter = m.begin();&lt;br /&gt;  &lt;br /&gt;  // loop over all distances&lt;br /&gt;  while(iter != m.end()) {&lt;br /&gt;    double d = iter-&amp;gt;first;&lt;br /&gt;  &lt;br /&gt;    // go over all distances for all planets &amp;lt;= to this one&lt;br /&gt;    while(iter != m.end() &amp;&amp; iter-&amp;gt;first &amp;lt;= d) {&lt;br /&gt;      int i1 = iter-&amp;gt;second.first;&lt;br /&gt;      int i2 = iter-&amp;gt;second.second;&lt;br /&gt;      Planet * p1 = &amp;planets[i1];&lt;br /&gt;      Planet * p2 = &amp;planets[i2];&lt;br /&gt;&lt;br /&gt;      // if the political situation is the same, &lt;br /&gt;      // increase the stabilization factor&lt;br /&gt;      if(p1-&amp;gt;p == p2-&amp;gt;p) {&lt;br /&gt;          p1-&amp;gt;c++;&lt;br /&gt;         // planet became stable so decrease destab&lt;br /&gt;         if(p1-&amp;gt;c == 0) destab--;&lt;br /&gt; &lt;br /&gt;         // make sure the second planet is &lt;br /&gt;         // different from the first&lt;br /&gt;         if(i1 != i2) {&lt;br /&gt;            p2-&amp;gt;c++;&lt;br /&gt;            // planet became stable so decrease destab&lt;br /&gt;            if(p2-&amp;gt;c == 0) destab--;&lt;br /&gt;         }&lt;br /&gt;      }&lt;br /&gt;      else {&lt;br /&gt;           // political situation is different &lt;br /&gt;           // so decrease factor&lt;br /&gt;           p1-&amp;gt;c--;&lt;br /&gt;           p2-&amp;gt;c--;&lt;br /&gt;&lt;br /&gt;           // we switched to unstable, increase destab&lt;br /&gt;           if(p1-&amp;gt;c == -1) destab++;&lt;br /&gt;           if(p2-&amp;gt;c == -1) destab++;&lt;br /&gt;      }&lt;br /&gt;&lt;br /&gt;      iter++;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // keep track of best solution&lt;br /&gt;    if(destab &amp;gt; best) {&lt;br /&gt;      best = destab;&lt;br /&gt;      bestR = d;&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I also think problem D. Data Mining is pretty easy.  It looks a bit intimidating because it has a bunch of math formulas. Your job is to find the minimum amount of memory that needs to be allocated based on a particular formula they give you.  The formula has an A and B parameter, which is not given and you have to find.  Everything else for the formula is given.  &lt;br /&gt;&lt;br /&gt;The key is to realize that since A and B are used as bit shift operators in the formula there is a limited number of valid A and B values.  This is because you can only shift an integer to the left and right so much before you just end up with a bunch of zero bits.  Thus, you can try all A and B combinations, plugging them into the memory formula and keeping track of the smallest number you can generate.  That number is your answer.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-6386851103303835199?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/6386851103303835199/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/02/im-idiot.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/6386851103303835199'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/6386851103303835199'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/02/im-idiot.html' title='I&apos;m an idiot'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-3877876601758715133</id><published>2009-02-19T12:57:00.000-08:00</published><updated>2009-02-19T13:13:39.596-08:00</updated><title type='text'>Practice with Waterloo</title><content type='html'>I had Dan and Scott join the University of Waterloo's online 5 hour practice session yesterday.  John couldn't make it, so I helped out more than I normally would.  I didn't write solutions to any of the problems, but I helped with debugging, reading the problems and some problem solving.  The contest problems were taken from the &lt;a href="http://neerc.ifmo.ru/past/2003.html"&gt;Northeastern European Regional 2003&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The guys solved 5 of 11 problems and attempted 6.  Not bad considering they were down a member, had never worked in a team before, and had some major technical difficulties at the start of the competition.  &lt;br /&gt;&lt;br /&gt;We weren't able to connect properly to Waterloo's contest software from UVic because of firewall issues.  We ended up having to connect through VNC and transfer files for submission via scp.  It wasn't until 80 minutes into the contest before the guys could submit the first few problems that they had solved.&lt;br /&gt;&lt;br /&gt;It was a pretty good problem set.  There were five relatively easy problems requiring simple implementation or some kind of bruteforce.  The guys probably started on the second easiest problem rather than the easiest.  Most teams started with problem A, which Dan eventually did in just a few minutes, but he didn't realize how easy it was at the beginning.  Without the contest software connection issues, the guys probably would have had three problems solved in the first hour.&lt;br /&gt;&lt;br /&gt;Things slowed down a bit after those first three as Dan tried a bruteforce attempt on problem H, which resulted in a time limit exceeded.  He then went on to work on problem E and Scott coded problem G and had it accepted with about 10 minutes left.&lt;br /&gt;&lt;br /&gt;There were a few other problems that looked reasonable.  I wrote a solution to problem J this morning, which can be solved with backtracking and branch bounds when you can't possibly improve on your best result.  A lot of teams solved H, but I have not been able to come up with an algorithm fast enough for the bounds of the problem.  There were also two graph problems, which most of the top teams did.  &lt;br /&gt;&lt;br /&gt;Waterloo solved 10 of 11 problems, which is quite impressive.  They are typically one of the best teams in the world, so it's not surprising.  I hope I can get all three members of the team to compete in one of Waterloo's weekend practices before I leave for Asia in March.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-3877876601758715133?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/3877876601758715133/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/02/practice-with-waterloo.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/3877876601758715133'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/3877876601758715133'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/02/practice-with-waterloo.html' title='Practice with Waterloo'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-8605285200179667103</id><published>2009-02-13T08:54:00.000-08:00</published><updated>2009-02-13T09:26:30.673-08:00</updated><title type='text'>Robocode and SRM 435</title><content type='html'>I moved this week's programming club meeting to Wednesday so that I could compete on &lt;a href="http://www.topcoder.com"&gt;TopCoder&lt;/a&gt; on Thursday.  This week at the meeting I introduced people to &lt;a href="http://robocode.sourceforge.net/"&gt;Robocode&lt;/a&gt;, which is an environment and API for developing little tank robots.  You can control the robot tank's radar, gun, and movement.  The goal is to program the best strategy such that you beat all the other robots.  &lt;br /&gt;&lt;br /&gt;I had played around with Robocode several years ago and programmed a few different tanks.  I thought it might be a nice break for everyone to try another style of programming, rather than strictly problem solving.  &lt;br /&gt;&lt;br /&gt;After I showed everyone how to build a basic robot, I got the students to start building their robots based on tutorials available on the Robocode Wiki.  Next week we will start the meeting off by having our various creations battle each other.&lt;br /&gt;&lt;br /&gt;I've been working a bit on my own robot.  It hasn't been as effective as I had hoped.  I implemented a force directed graph layout to control my robot's movements.  Bullets that I detect, along with enemies are forces that push my robot in certain directions.  It's reasonably effective, meaning I can defeat all the sample bots, but the behavior is a bit erratic at times and doesn't always make sense to me.  I may need to do some tweaking.  I've also been working on using a different strategy for one on one situations.&lt;br /&gt;&lt;br /&gt;Last night I competed in SRM 435 on TopCoder.  I solved two of the three problems, however I was considerably slower than need be.  I had some computer issues that slowed down my compile and run times, but I also made a number of silly mistakes.  &lt;br /&gt;&lt;br /&gt;In the first problem, when processing two strings consisting of digits (0-9), I forgot to convert the char representations into integers when performing my math calculations.  It didn't take long to fix, but still cost me a minute or two.&lt;br /&gt;&lt;br /&gt;The second problem was pretty simple.  It boiled down to having the description of a binary tree, and you had to delete a single node and then determine how many leaf nodes are left.  I represented the tree using a vector of vectors, then did a breadth first search starting at the deleted node and marking all visited nodes for deletion.  Then I iterated over all nodes and counted how many didn't have children and that still existed in my graph.  &lt;br /&gt;&lt;br /&gt;I lost several minutes on this problem due to a stupid mistake.  I misread the first sample test case, so when my program first ran I thought I was doing something wrong, but it was in fact working.  I spent a few minutes debugging something that wasn't a bug!  I eventually submitted the problem, but I should have been able to get more points for it.&lt;br /&gt;&lt;br /&gt;In retrospect, it would have been less code to do this recursively and start at the root, not recurse on the deleted node, and return all leaf nodes encountered.  &lt;br /&gt;&lt;br /&gt;I had ample time for the last problem, but I wasn't able to come up with a decent algorithm.  It was a probability problem.  I tried a randomized algorithm after being stuck for intelligent ideas, but it wasn't accurate enough.  The actual solution involved dynamic programming and I probably should have realized this.&lt;br /&gt;&lt;br /&gt;The next TopCoder competition I will be competing in is on the 28th and is a qualification round for the TopCoder Open.  I don't really expect to make it very far in this tournament, but it will be good practice.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-8605285200179667103?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/8605285200179667103/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/02/robocode-and-srm-435.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/8605285200179667103'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/8605285200179667103'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/02/robocode-and-srm-435.html' title='Robocode and SRM 435'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-2331913687728152222</id><published>2009-02-06T11:39:00.000-08:00</published><updated>2009-02-06T11:57:38.854-08:00</updated><title type='text'>Team Practice Session</title><content type='html'>I tried doing a team practice session last night during our weekly PClub meeting.  It didn't go as well as I had hoped.  Several people had to miss practice for various reasons and some people showed up late, so I wasn't able to put together the teams I had planned.  &lt;br /&gt;&lt;br /&gt;I had one team work on problems from last year's &lt;a href="http://acmicpc-live-archive.uva.es/nuevoportal/region.php?r=nw&amp;year=2008"&gt;Northwestern Europe Regional&lt;/a&gt; while the other two teams worked on the five easiest problems (numbers 4065, 4067, 4069, 4071, and 4072) from the &lt;a href="http://acmicpc-live-archive.uva.es/nuevoportal/region.php?r=pn&amp;year=2007"&gt;2007 Pacific Northwest Regional&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Both teams working on the five easier problems solved 4065, which is a simple game simulation for Rock, Paper, and Scissors.  One of the teams also submitted 4072, which is another game simulation problem, but got a wrong answer on it.  After I fixed that team's parsing, their code worked.  &lt;br /&gt;&lt;br /&gt;Meanwhile, the team working on the harder problems solved two and attempted a third.  However, the team aspect of these "teams" didn't really work.  All submissions were sent by the same coder and I'm not sure everyone really got the idea of writing their code out on paper first.  It's really important to actually implement the code to solve the problem on paper first while someone else is using the computer.  &lt;br /&gt;&lt;br /&gt;This is probably somewhat difficult for most of the first years as they struggle with syntax issues and depend on the compiler to check their syntax.  They were also struggling with coming up with correct algorithms for the problems, so things moved along slowly.&lt;br /&gt;&lt;br /&gt;Next week we are taking a bit of a break from the problem solving stuff to do some &lt;a href="http://robocode.sourceforge.net/"&gt;Robocode&lt;/a&gt;.  I'm planning on having everyone write the AI to their own tanks and the following week we'll do a little competition.  It's just for fun.  I may even participate.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-2331913687728152222?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/2331913687728152222/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/02/team-practice-session.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/2331913687728152222'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/2331913687728152222'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/02/team-practice-session.html' title='Team Practice Session'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-6983089874644744125</id><published>2009-02-02T08:37:00.001-08:00</published><updated>2009-02-05T09:30:29.378-08:00</updated><title type='text'>First Annual UVic Programming Competition</title><content type='html'>The First Annual UVic Programming Contest took place last Saturday, January 31st, 2009.  The contest went extremely smooth.  We didn't have any problems with the &lt;a href="http://www.ecs.csus.edu/pc2/"&gt;PC^2 software&lt;/a&gt; and only a few minor issues with the problem set, but everything was caught right away.  There were 12 competitors in all.  You can check out the &lt;a href="http://webhome.cs.uvic.ca/~seanf/contest/01312009/"&gt;problem set, scoreboard, solutions, and judge's inputs/outputs here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;There was some pretty exciting back and forth action between the top three competitors.  Both Dan Sanders and Scott Porter jumped out to an early lead by solving the first problem at the 8 minute mark.  Dan solved a second problem 12 minutes later and had the lead through the first three problems.  John Hawthorn took the lead at 51 minutes by solving his fourth problem.  &lt;br /&gt;&lt;br /&gt;At this point, we were really worried that the top three competitors would solve all the problems in half the time meaning that we made the problem set too easy.  We knew that the top competitors would probably solve the first three in the first hour, but they were going even faster than we anticipated.  &lt;br /&gt;&lt;br /&gt;Scott jumped into second place ahead of Dan by solving a 4th problem at the 86 minute mark, but Dan jumped back in front of Scott 15 minutes later by solving one of the hardest problems in the set.  John beat everyone to a 5th problem by getting an accepted solution on the Gem Collector on his 4th attempt.  Dan and Scott caught up, but with around 2 hours left in the competition John solved his 6th problem on his 8th attempt.  &lt;br /&gt;&lt;br /&gt;Dan caught up with over an hour left, but he needed to fix his Gem Collect problem in order to retake the lead.  With approximately 50 minutes left, John submitted an attempt at G.  I promptly took the scoreboard down for suspense and then went and judged his problem.  He got a wrong answer, but 10 minutes later, he submitted again and had it accepted. &lt;br /&gt;&lt;br /&gt;Both Dan and Scott attempted to get a 7th problem, but neither managed to get their solutions accepted.  Everyone did extremely well.  Only one person solved all the problems and everyone solved at least one.  Most the competitors were in first year, so that's a fantastic result.&lt;br /&gt;&lt;br /&gt;Below is a brief description of how to solve each problem.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;A. [**SPAM**] Congratulations, You've Won!!!&lt;/span&gt;&lt;br /&gt;Once you read the story, the problem boils down to finding the largest prime number less than or equal to some value N.  The maximum value of N is small enough that just about any solution would be acceptable.  One simple way to solve the problem is read in N and then test primality for all values less than or equal to N.  Something like the following.&lt;br /&gt;&lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;int i;&lt;br /&gt;for(i = N; i &amp;gt;= 2; i--) {&lt;br /&gt;  if(isPrime(i)) break;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;print(i);&lt;br /&gt;&lt;br /&gt;bool isPrime(int t) {&lt;br /&gt;  for(int i = 2; i*i &amp;lt;= t; i++) {&lt;br /&gt;     if(!(t % 2)) return false;&lt;br /&gt;  }&lt;br /&gt;  return true;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;B. The Love Guru&lt;/span&gt;&lt;br /&gt;The Love Guru was of similar complexity to the first problem.  All that was required was to read in two strings, count the number of shared letters between the two strings (case-insensitive) and divide that by the total number of unique letters.  If that value was greater than or equal to 0.4, then print "Get hitched", otherwise print "Part ways".&lt;br /&gt;&lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;string getRecommendation(string n1, string n2) {&lt;br /&gt;    // use a vector to check if we've seen a letter before&lt;br /&gt;    vector&amp;lt;int&amp;gt; u(26, 0);&lt;br /&gt;    int shared = 0, total = 0;&lt;br /&gt;    // total up unique letters in first name&lt;br /&gt;    for(int i = 0; i &amp;lt; s1.length(); i++) {&lt;br /&gt;      if(!u[s1[i]-'a']) total++;&lt;br /&gt;      u[s1[i]-'a'] = 1;&lt;br /&gt;    }&lt;br /&gt;   &lt;br /&gt;    // total up shared and unique letters from second name&lt;br /&gt;    for(int i = 0; i &amp;lt; s2.length(); i++) {&lt;br /&gt;      if(u[s2[i]-'a'] == 1) { shared++; }&lt;br /&gt;      else if(!u[s2[i]-'a']) total++;&lt;br /&gt;      u[s2[i]-'a'] = 2;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // compute percent with truncation&lt;br /&gt;    int perc = (int)((double)shared/(double)total*100.0);&lt;br /&gt;&lt;br /&gt;    return ((perc &amp;gt;= 40) ? "Get hitched" : "Part ways");&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;C. You Sunk My Battleship&lt;/span&gt;&lt;br /&gt;This was the third easy problem in the set, which we expected most people to attempt or solve.  The problem was originally a bit trickier, where there were more ships on the board of varying sizes, but we decided to make the parsing easier.  You had to read the description carefully and implement a single player game simulation for Battleship.  &lt;br /&gt;&lt;br /&gt;The main problem people had was not realizing that a hit to the same location more than once should print "Hit" multiple times but not necessarily sink the ship.  You have to hit the battleship in all four locations at least once to sink the ship.  &lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;D. Great Ocean Views!&lt;/span&gt;&lt;br /&gt;This was the first problem in the set that required a bit more insight to easily solve.  Everyone that solved this problem got it accepted on the first try, so provided you were lucky enough to see how to approach the problem, the algorithm was easy to implement.  The first key is to realize you can ignore the Z dimension.  The second key is that since everything is a rectangle defined in integer space and the size is a maximum 50x50, you can define a matrix to store the window locations of all buildings.  You "paint" the building rectangle onto this matrix and afterwards sum up all the overlapping windows.  Something like the following.&lt;br /&gt;&lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;int matrix[51][51];&lt;br /&gt;// initialize all values to -1&lt;br /&gt;memset(matrix, -1, sizeof(int)*51*51);&lt;br /&gt;&lt;br /&gt;int x, w, h, z;&lt;br /&gt;// read in and process each building&lt;br /&gt;for(int i = 0; i &amp;lt; N; i++) {&lt;br /&gt;  cin &amp;gt;&amp;gt; x &amp;gt;&amp;gt; w &amp;gt;&amp;gt; h &amp;gt;&amp;gt; z;&lt;br /&gt;  // paint the building onto the matrix&lt;br /&gt;  for(int j = x; j &amp;lt; x + w; j++) &lt;br /&gt;     for(int k = 0; k &amp;lt; h; k++) matrix[j][k]++;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// sum the windows blocked&lt;br /&gt;int windowsBlocked = 0;&lt;br /&gt;for(int i = 0; i &amp;lt; 51; i++) {&lt;br /&gt;  for(int j = 0; j &amp;lt;51; j++) &lt;br /&gt;     windowsBlocked += matrix[i][j];&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;print(windowsBlocked);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;E. Gem Collector&lt;/span&gt;&lt;br /&gt;This problem describes a game in which you collect gems from a matrix.  The gems have different values, and you want to determine what is the maximum value of gems that you can collect given that you can start in any location and collect from any cell adjacent to a cell where you collected a gem previously.&lt;br /&gt;&lt;br /&gt;Several people had problems with getting an accepted solution.  A lot of solutions didn't run under the time limit or gave run time errors.  Also, some people seemed to misinterpret what was required to solve the problem.  The gem matrix is a maximum 25x25, so it's possibly to try all  starting locations and recursively collect gems, keeping track of the maximum value you can collect.  See &lt;a href="http://webhome.cs.uvic.ca/~seanf/contest/01312009/E.cpp"&gt;my solution&lt;/a&gt; for more details.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;F. Vegas Baby!&lt;/span&gt;&lt;br /&gt;Only the top three people solved this problem.  The solution requires dynamic programming.  It's a pretty standard dynamic programming problem, so if you were familiar with the concept, it was likely you would solve it.  There was some ambiguity with the problem that caused people to get wrong answers.  They weren't sure which value for the number of people to return if there were multiple solutions that cost the same.  We wanted the number of people to be as close to K as possible.  &lt;br /&gt;&lt;br /&gt;To solve the problem, you can make an array or vector where each index represents how many people you can bring, and in each entry you store the minimum cost to bring that many people.  To fill this array, you can process each university and for each existing entry in the array, combine that entry with the current university you are processing.  Also, update all index values that are a multiple of the number of people from the current university.&lt;br /&gt;&lt;br /&gt;The solution is something like the following, where we have a vector called makes initialized with INT_MIN, and the people and cost for a university are stored in arrays p and c.&lt;br /&gt;&lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;// loop through our participants&lt;br /&gt;for(int i = 0; i &amp;lt; N; i++) {&lt;br /&gt;    // loop through our current combinations&lt;br /&gt;    for(int j = 0; j &amp;lt; makes.size(); j++) {&lt;br /&gt;        // check if we have a combination for j, &lt;br /&gt;        // if so, use it to figure out j + p[i]&lt;br /&gt;        if(j + p[i] &amp;lt; makes.size() &amp;&amp; makes[j] != INT_MAX) {&lt;br /&gt;           makes[j+p[i]] = min(makes[j+p[i]], makes[j] + c[i]);&lt;br /&gt;        }  // j is divisble by p[i], so we can make j&lt;br /&gt;        else if(!(j % p[i])) { &lt;br /&gt;           makes[j] = min(makes[j], c[i] * j/p[i]);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;G. Help Sean&lt;/span&gt;&lt;br /&gt;The last problem, once deciphered, works out to be a pretty standard graph problem.  Each city can be considered as a node in a graph, edges exist between cities provided the distance between the two cities is less than or equal to the distance you can travel with the car's gas tank.  Then you need to calculate the shortest path between the start city (i.e. Victoria) and the end city (i.e. St. Stephen).  This can be done using &lt;a href="http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm"&gt;Dijkstra's algorithm&lt;/a&gt; or &lt;a href="http://en.wikipedia.org/wiki/Floyd-Warshall_algorithm"&gt;All-pairs Shortest Path&lt;/a&gt; or the &lt;a href="http://en.wikipedia.org/wiki/Bellman-Ford_algorithm"&gt;Bellman Ford Algorithm&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;There was also a bit of string parsing to read in the city names and you had to properly implement the given formulas for calculating the distance between latitude and longitude coordinates taking into account the spherical nature of the Earth.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-6983089874644744125?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/6983089874644744125/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/02/first-annual-uvic-programming.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/6983089874644744125'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/6983089874644744125'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/02/first-annual-uvic-programming.html' title='First Annual UVic Programming Competition'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-1700885189611144717</id><published>2009-01-30T06:55:00.000-08:00</published><updated>2009-01-31T08:52:10.043-08:00</updated><title type='text'>Practice Sessions - Week 4</title><content type='html'>Last night was the last practice session before the UVic undergraduate programming competition on Saturday.  We had a decent turn out of 11 people and I heard from a few people that are coming to the competition but couldn't make it last night.  I'm guessing we'll have about 15 people on the weekend.&lt;br /&gt;&lt;br /&gt;I had the students do some old contest problems that &lt;a href="http://www.smilingpolitely.org/"&gt;Nathan Scott&lt;/a&gt; and I wrote back at UNB.  There were five problems in total, both Dan and Scott solved three, a few solved two and everyone solved at least one.  Dan got a fourth problem completed after the end of the practice.&lt;br /&gt;&lt;br /&gt;The first three problems were pretty easy, but trickier than the easiest problems I've had the students do in the past.  In the first, you have to simulate the process of determining who won a programming contest.  This involved reading in people's names, the time it took them to solve a given problem and how many submissions it took.  Then you needed a little logic to calculate their total problems solved plus time taken. &lt;br /&gt;&lt;br /&gt;We had some unforeseen issues with this problem.  It turned out there was a mistake in the judge's input where one case was larger than what was specified in the problem.  This was causing a lot of people's code to either crash or run forever.  Unfortunately, we didn't realize this until an hour or so of the student's spinning their heels trying to debug something that wasn't a bug.&lt;br /&gt;&lt;br /&gt;The second problem was actually probably the easiest to code in the bunch.  It had several scary math formulas in the description, which scared most people off.  However, if you take the time to read the problem carefully, all you need to do is implement the last formula that is described.  This involved reading in a bunch of numbers, multiplying them together, seeing which one was largest, and printing it out with 5 decimal places.  Eventually, several of the students realized this and solved it quickly.&lt;br /&gt;&lt;br /&gt;The third problem was a simulation problem.  You had to read in a list of book inventory and then process orders on a first come first serve basis and print how much the store made through the sales or a line indicating that they could not fulfill the orders.  Understanding what is required in this problem is reasonably straight-forward, but the problem requires some delicate string parsing to read in the book titles.  Also, the inventory list has to be read into some kind of data structure and stored for look up during the ordering phase.  &lt;br /&gt;&lt;br /&gt;A simple way to do this is to create a Book struct or class that contains the name, current quantity and price of a given book then store these in a vector or perhaps a map.&lt;br /&gt;&lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;struct Book {&lt;br /&gt; string title;&lt;br /&gt; int q, p;&lt;br /&gt; Book(string title, int q, int p) : title(title), q(q), p(p) {}&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;vector&amp;lt;Book&amp;gt; books;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The number of books is small, so all books can be stored in a vector and look-up can be an O(n) call.  In my opinion, the solution to this problem is much easier to code in C++ or Java, as both have dynamic data structures built into the language like lists, vectors, maps, etc.  Also, I think the input parsing is easier in those languages.&lt;br /&gt;&lt;br /&gt;The next problem was about magic squares.  A magic square is an arrangement of numbers from 1 to n^2 in an n*n matrix, with each number occurring exactly once, and such that the sum of the entries of any row, column, or main diagonal is the same.  Here is an example of one:&lt;br /&gt;&lt;style type="text/css"&gt;.nobrtable br { display: none }&lt;/style&gt;&lt;br /&gt;&lt;div class="nobrtable"&gt;&lt;br /&gt;&lt;table border="1" cellpadding="2"&gt;&lt;br /&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align="center"&gt;8&lt;/td&gt;&lt;td align="center"&gt;1&lt;/td&gt;&lt;td align="center"&gt;6&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td align="center"&gt;3&lt;/td&gt;&lt;td align="center"&gt;5&lt;/td&gt;&lt;td align="center"&gt;7&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td align="center"&gt;4&lt;/td&gt;&lt;td align="center"&gt;7&lt;/td&gt;&lt;td align="center"&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Other magic squares exist for larger numbers, although the numbers are still consecutive within the square.  In the problem, you have to attempt to complete a square given a partial configuration.  For example:&lt;br /&gt;&lt;style type="text/css"&gt;.nobrtable br { display: none }&lt;/style&gt;&lt;br /&gt;&lt;div class="nobrtable"&gt;&lt;br /&gt;&lt;table border="1" cellpadding="2"&gt;&lt;br /&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align="center"&gt;24&lt;/td&gt;&lt;td align="center"&gt;X&lt;/td&gt;&lt;td align="center"&gt;X&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td align="center"&gt;X&lt;/td&gt;&lt;td align="center"&gt;X&lt;/td&gt;&lt;td align="center"&gt;23&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td align="center"&gt;20&lt;/td&gt;&lt;td align="center"&gt;25&lt;/td&gt;&lt;td align="center"&gt;X&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;There is more than one way to go about this problem.  The completely bruteforce way is to realize that you can try all possible sequences of 9 consecutive numbers that fit within the given numbers in the square and all permutations of these.  This is going to be proportional to 9! = 362,880, which will get under the time limit.&lt;br /&gt;&lt;br /&gt;A less bruteforce approach is to observe that all larger magic squares can be computed based on the 1 to n^2 example.  Consider the following magic square:&lt;br /&gt;&lt;style type="text/css"&gt;.nobrtable br { display: none }&lt;/style&gt;&lt;br /&gt;&lt;div class="nobrtable"&gt;&lt;br /&gt;&lt;table border="1" cellpadding="2"&gt;&lt;br /&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td align="center"&gt;19&lt;/td&gt;&lt;td align="center"&gt;12&lt;/td&gt;&lt;td align="center"&gt;17&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td align="center"&gt;14&lt;/td&gt;&lt;td align="center"&gt;16&lt;/td&gt;&lt;td align="center"&gt;18&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td align="center"&gt;15&lt;/td&gt;&lt;td align="center"&gt;20&lt;/td&gt;&lt;td align="center"&gt;13&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;All values (i,j) in this square are equivalent to the original square cell values (i,j) + 11.  This is true for all magic squares, they all can be derived from the first by adding some constant factor.  Also, the original square has multiple configurations, which can be generated by rotations or flips of the original matrix.&lt;br /&gt;&lt;br /&gt;Thus, to solve this problem, you can try all rotations and flips of the original matrix, check that the given values minus the original matrix values are equal, add this difference to all unknown cells and check if the matrix is a magic square.  The easiest way to do the rotations and flips to compute all permutations of the array 1 through 9.  We can easily do this in C++ in a few lines.  The solution for magic squares would look something like the following.  &lt;br /&gt;&lt;br /&gt;Assume the unknown matrix is stored in an array of chars called matrix[].  Also, assume we have a function called isMagicSquare that will check whether a given array is a valid magic square configuration.&lt;br /&gt;&lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;char matrix[];&lt;br /&gt;int v[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};&lt;br /&gt;do {&lt;br /&gt; if(isMagicSquare(v)) {&lt;br /&gt;   int diff = (matrix[0] - '0') - v[0];&lt;br /&gt;   int i = 1;&lt;br /&gt;   for(; i &amp;lt; 9; i++) {&lt;br /&gt;     if(matrix[i] != 'X' &amp;&amp; diff != (matrix[i] - '0') - v[i]) {&lt;br /&gt;       break;&lt;br /&gt;     }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   if(i == 9) {&lt;br /&gt;      printSolution(v, matrix);&lt;br /&gt;      break;&lt;br /&gt;   }&lt;br /&gt;} while(next_permutation(v, v+9));&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The last problem was called "Building teams".  Dan eventually solved this problem after the practice session was complete.  After you decipher the problem, it boils down to graph coloring with at most four colors.  Each clique of students is a node and a node is adjacent to another node if the clique's dislike each other.  You have to attempt to color the nodes such that adjacent nodes do not have the same color.  Since graph coloring with more than two colors is NP-Complete, this should give you a hint that an inefficient backtracking algorithm should work.  Even though there are at most 30 nodes, the colorings prune quite quickly as many of the 4^30 possibilities are equivalent.&lt;br /&gt;&lt;br /&gt;Assume we have an array called nodeColors, which we will store all our colors in.  Also assume we have a function called validColoring, which makes sure a given color assignment is valid, that is no adjacent nodes have the given color.  Then we can color the nodes recursively doing something like the following.&lt;br /&gt;&lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;// our node colors&lt;br /&gt;int nodeColors[30];&lt;br /&gt;&lt;br /&gt;// recursive function to assign colors, currentIndex is the current node&lt;br /&gt;// maxColors is the max colors we are going to try&lt;br /&gt;bool assignColors(int currentIndex, int maxColors) {&lt;br /&gt; if(currentIndex &amp;gt; N) return true;&lt;br /&gt;&lt;br /&gt; // try assigning all colors&lt;br /&gt; for(int i = 0; i &amp;lt; maxColors; i++) {&lt;br /&gt;    // make sure this is a valid coloring for this node&lt;br /&gt;    if(validColoring(currentIndex, i)) {&lt;br /&gt;       nodeColors[currentIndex] = i;&lt;br /&gt;       if(assignColors(currentIndex+1, maxColors)) {&lt;br /&gt;          return true;&lt;br /&gt;       }&lt;br /&gt;       nodeColors[currentIndex] = -1;&lt;br /&gt;    }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; return false;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next, call this function with maxColors equal to 1, 2, 3, and 4.  The first one that returns true is the number of teams, if none return true, then you can't build the teams.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-1700885189611144717?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/1700885189611144717/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/01/practice-sessions-week-4.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/1700885189611144717'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/1700885189611144717'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/01/practice-sessions-week-4.html' title='Practice Sessions - Week 4'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-816046945476291776</id><published>2009-01-28T13:09:00.001-08:00</published><updated>2009-03-05T09:06:49.244-08:00</updated><title type='text'>Knowing your libraries</title><content type='html'>Since I've started competing on &lt;a href="http://www.topcoder.com"&gt;TopCoder&lt;/a&gt; the lesson of knowing your libraries has been really hammered home for me.  I was, of course, never against this, but often I would write my own functions to check alphanumeric values, parse integers and doubles, format output, and so forth.  These don't take that much time to write, but it's still time away from writing the actual solution to the problem.  Also, there's always a chance you'll make a typo or logic error, which can be costly.&lt;br /&gt;&lt;br /&gt;I worried less about this when I use to compete in the ACM competitions because since it's 5 hours, it's almost more like a marathon than a sprint.  However, on TopCoder, you generally know that the top people are going to solve all 3 problems, the only way to beat them is by being faster.  &lt;br /&gt;&lt;br /&gt;This can be true for the ACM competitions as well.  This year at the Pacific Northwest Regional, something like 10 teams solved 7 problems, but only one of those teams qualified for the world finals.  Because UBC was at the top of that heap, they are off to Stockholm for the finals.&lt;br /&gt;&lt;br /&gt;Recently, in a practice competition on TopCoder I did a problem where you're given a string and you need to return a vector containing two substrings of the original string.  The first substring has to have the same number of a's as the number of b's in the second substring.&lt;br /&gt;&lt;br /&gt;This is pretty easy to solve.  I did something like the following:&lt;br /&gt;&lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;vector&lt;string&gt; getSubstrings(string str) {&lt;br /&gt;  string x, y;&lt;br /&gt;  x = str;&lt;br /&gt;&lt;br /&gt;  while(1) {&lt;br /&gt;    int c1 = count(x, 'a');&lt;br /&gt;    int c2 = count(y, 'b');&lt;br /&gt; &lt;br /&gt;    if(c1 == c2) break;&lt;br /&gt;&lt;br /&gt;    y = x[x.size()-1] + y;&lt;br /&gt;    x = x.substr(0, x.size()-1);&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  vector&lt;string&gt; ans;&lt;br /&gt;  ans.push_back(x);&lt;br /&gt;  ans.push_back(y);&lt;br /&gt;&lt;br /&gt;  return ans;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The key here is the "count" function.  I actually wrote my own count function.  Sure, it's just a for loop, but it's an extra 4-5 lines of code.  There's actually a count function built into the C++ STL.  The fastest solution for this SRM used this function.  With this function, my loop above could be something like this:&lt;br /&gt;&lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;while(count(x.begin(), x.end(), 'a') &lt;br /&gt;        != count(y.begin(), y.end(), 'b')) { ... }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I scored something like 245 points on this problem, but by using a few language built-ins, I probably could have had 247 or 248.  Oleg and I have actually had practices where we were separated by this small a margin, so every little bit helps.&lt;br /&gt;&lt;br /&gt;Another library I've recently opened my eyes to is the C++ istringstream. It's great for parsing input for certain types of problems.  For example, if you had to read in a list of comma separated doubles like "-234.34, 324.234, 3243.22232,876.435", you can quickly parse this with istringstream.  It works similar to cin.&lt;br /&gt;&lt;br /&gt;Example:&lt;br /&gt;&lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;string s  = "-234.34, 324.234, 3243.22232,876.435";&lt;br /&gt;// replace commas with spaces&lt;br /&gt;replace(s.begin(), s.end(), ',', ' ');&lt;br /&gt;&lt;br /&gt;istringstream ss(s);&lt;br /&gt;double d;&lt;br /&gt;while(ss &gt;&gt; d) {&lt;br /&gt;  cout &lt;&lt; d &lt;&lt; endl;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You could of course do this by processing the string character by character, but that can be messy and error prone.&lt;br /&gt;&lt;br /&gt;The stringstream class is also useful and works similar to cout.  One use is to convert doubles into strings.&lt;br /&gt;&lt;br /&gt;Example:&lt;br /&gt;&lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;// create a double&lt;br /&gt;double d = 1.22423;&lt;br /&gt;&lt;br /&gt;// create a stringstream and put our double into the stream&lt;br /&gt;stringstream ss;&lt;br /&gt;ss &lt;&lt; d;&lt;br /&gt;&lt;br /&gt;// convert our stream into a string&lt;br /&gt;string s = ss.str();&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-816046945476291776?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/816046945476291776/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/01/knowing-your-libraries.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/816046945476291776'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/816046945476291776'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/01/knowing-your-libraries.html' title='Knowing your libraries'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-6606287186570926037</id><published>2009-01-28T10:53:00.000-08:00</published><updated>2009-01-28T13:09:06.320-08:00</updated><title type='text'>TopCoder Practice - SRM 308</title><content type='html'>I did a practice SRM on &lt;a href="http://www.topcoder.com"&gt;TopCoder&lt;/a&gt; this morning with my friend &lt;a href="http://publish.uwo.ca/~ogolubit/"&gt;Oleg&lt;/a&gt;.  It was &lt;a href="http://www.topcoder.com/stat?c=round_overview&amp;er=5&amp;rd=9988"&gt;SRM 308&lt;/a&gt; Div 2.  It went reasonably well, we both got 2 out of 3 accepted.  Oleg submitted the 1000 point problem but made a simple mistake by calling the min function rather than max function in one place.  I didn't end up submitting the 1000.  Oleg beat me by about 20 points as he finished the second problem faster than me.&lt;br /&gt;&lt;br /&gt;The first problem was probably one of the easiest 250 point problems I've done on TC and I got it submitted faster than any problem I've done before (249.30 points).  The problem was given a vector of integers, return the median or -1 if no median exists.  I simply sorted the vector, if the size of the vector was odd, return v[v.size()/2], otherwise return -1.&lt;br /&gt;&lt;br /&gt;The second problem was with decoding strings that were encoded using Huffman codes.  In Huffman codes, characters in a string are replaced by bit strings.  A Huffman code has the property that no bit string representation of a character is a prefix of another bit string.  So, for example, the following are valid codes: A="00", B="10", C="01", and D="111", but A="00", B="0010", C="01", and D="111" is not because the encoding for A is a prefix to B.&lt;br /&gt;&lt;br /&gt;Using the valid codes above we can encode the string ABACD as "00100001111".  In the problem, you're given the codes plus the encoded string and you have to decode it.&lt;br /&gt;&lt;br /&gt;This is pretty straightforward.  You can loop through your encoded string, loop through your dictionary and try to match the dictionary word with the corresponding substring in your encoded string.  If they match, then that's the letter you want.  Save that letter and move your current index in your encoded string ahead by the length of the dictionary word. Continue this until your current index is greater than the length of your encoded string.&lt;br /&gt;&lt;br /&gt;The third problem, which I didn't have time to complete, required a combination of dynamic programming (DP) with greedy.  You have a bunch of treasure items, which have a cost value and weight value.  You want to maximize the total cost of collecting items but not collect more than W amount of weight.  This is a pretty classic DP problem.  The catch is that in this problem, some treasure items can be divided into smaller pieces.  For example, you could take 10% of an item or 30% or whatever.&lt;br /&gt;&lt;br /&gt;I didn't read the problem as carefully as I should have so I immediately coded the DP part.  Then I tried to retrofit this to accommodate the case of using only partial amounts.  That didn't go all that well :-).  I was getting some of the sample solutions, but not all.  I eventually realized I needed to consider the full and partial items separately, but it was too late to finish that. &lt;br /&gt;&lt;br /&gt;The solution is to use DP with all items that can't be divided.  You do this by making a cost array, where the indices represent the weights.  You store in each index the maximum cost you can get for the given weight.  &lt;br /&gt;&lt;br /&gt;This is calculated as follows:&lt;br /&gt;&lt;br /&gt;Assume we have an array of non-divisible item weights called w and costs called c.&lt;br /&gt;&lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;for i = 0 to size(w) &lt;br /&gt;  for j = 0 to W&lt;br /&gt;     cost[j + w[i]] = max(cost[j + w[i]], cost[j] + c[i]);&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The second part is the greedy section.  You sort all divisible items by their weight to cost ratio.  Loop over your cost array and for each weight you've succeeded in making previously, loop over all divisible items.  For each divisible item, calculate the cost of adding that item to your current weight provided the combined weight is less than W.  After this inner loop, update your best cost if the cost you just made is better than any you've seen previously.&lt;br /&gt;&lt;br /&gt;It should look something like this:&lt;br /&gt;&lt;br /&gt;Assume we have an array of divisible items weights called wd and costs called cd, which have been sorted by their ratios.&lt;br /&gt;&lt;pre name="code" class="Cpp"&gt;&lt;br /&gt;bestCost = 0;&lt;br /&gt;for i = 0 to size(cost)&lt;br /&gt;  if cost[i] != 0&lt;br /&gt;     currentCost = cost[i] &lt;br /&gt;     weight = i;&lt;br /&gt;     for j = 0 to size(wd) &lt;br /&gt;         weightDiff = min(W - weight, wd[j])&lt;br /&gt;         currentCost = cd[j] * weightDiff / wd[j]&lt;br /&gt;         weight += weightDiff;&lt;br /&gt;         if(weight &gt;= W) break&lt;br /&gt;&lt;br /&gt;     bestCost = max(bestCost, currentCost)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Despite not getting the third problem, Oleg and I both would have been in the top 30 for this SRM.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-6606287186570926037?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/6606287186570926037/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/01/topcoder-practice-srm-308.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/6606287186570926037'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/6606287186570926037'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/01/topcoder-practice-srm-308.html' title='TopCoder Practice - SRM 308'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-6646059666313823482</id><published>2009-01-26T09:35:00.001-08:00</published><updated>2009-01-26T09:57:23.970-08:00</updated><title type='text'>Contest preparation</title><content type='html'>I posted this week's problem of the week challenge on the UVic programming club website.  You can check it out &lt;a href="http://www.cs.uvic.ca/~seanf/problems/grammarparsing.pdf"&gt;here&lt;/a&gt; if you're curious.  It's based in part on a problem I did years ago at one of my first contests.  It's not a really challenging problem, although some people may struggle with the parsing and this is in part why I chose this problem.  The students main hurdle at the moment is with knowing how to best read in input, so hopefully this problem will give them a little extra practice.&lt;br /&gt;&lt;br /&gt;The actual undergraduate competition that I am organizing is less than a week away, January 31st.  I still have a fair amount to do, like print off all the problems, instructions, login information, and organize it into envelopes.  I also need to get all the sample input files, links to APIs, and program submitting software (&lt;a href="http://www.ecs.csus.edu/pc2/"&gt;PC^2&lt;/a&gt;) on all the contestant accounts.  I plan to set up a scoreboard in the lab where we're hosting the contest using the projector and screens available.  &lt;br /&gt;&lt;br /&gt;One of the instructors at UVic is organizing the prizes and pizza for after the contest.  I plan to pick up some munchies in the morning on the way in so that people will have something to munch on prior to the competition. &lt;br /&gt;&lt;br /&gt;Unrelated to this.  I submitted my solution to the 500 point from the &lt;a href="http://www.topcoder.com"&gt;TopCoder&lt;/a&gt; SRM 433.  Passed with flying colors.  Ugh, depressing :-(.  (Check out &lt;a href="http://seanfalconer.blogspot.com/2009/01/topcoder-srm-433.html"&gt;my post from Jan. 21st&lt;/a&gt; for details).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-6646059666313823482?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/6646059666313823482/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/01/contest-preparation.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/6646059666313823482'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/6646059666313823482'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/01/contest-preparation.html' title='Contest preparation'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-6807851002286123512</id><published>2009-01-23T10:00:00.000-08:00</published><updated>2009-01-23T10:37:39.443-08:00</updated><title type='text'>Practice Sessions - Week 3</title><content type='html'>We had our third week of practice last night.  Approximately 10 people were there, so the numbers are staying consistent.  I'm hoping that we'll have about 15 for the competition on the 31st. &lt;br /&gt;&lt;a href="http://blog.ianbull.com/"&gt;&lt;br /&gt;Ian Bull&lt;/a&gt; showed up at the beginning to talk about the &lt;a href="http://code.google.com/soc/2008"&gt;Google Summer of Code&lt;/a&gt; project.  He participated last year and is possibly mentoring this coming summer.  We both thought the club members might be interested in the opportunity.&lt;br /&gt;&lt;br /&gt;At the meeting we first went over last weeks problems.  I did this rather quickly, so I'm not sure whether everyone followed my discussion.  I decided to have a little mini-contest last night.  In previous weeks, I've been splitting people up into experienced and newbies, where the experienced people work on harder problems, but this week everyone worked on the same set of four problems. &lt;br /&gt;&lt;br /&gt;I told everyone that the person that solves the most problems would win a prize.  The extravagant prize turned out to be a Snickers bar!  I mocked up a scoreboard on the whiteboard to keep track of everything. &lt;br /&gt;&lt;br /&gt;Dan Sanders ended up winning with three problems solved.  He solved the three in about an hour and fifteen minutes, but was stuck on the last.  I eventually told him to move onto solving the &lt;a href="http://www.cs.uvic.ca/%7Eseanf/problems/wordgame.pdf"&gt;problem of the week&lt;/a&gt; instead.  Unfortunately, most of the other experienced guys either had to come late or leave early, so Dan didn't have any major challengers.  Two others, Tristin and Kevin, solved two problems each, and everyone solved at least one. &lt;br /&gt;&lt;br /&gt;The four problems came from a mixture of problems I had used at old contests I ran at &lt;a href="http://www.unb.ca"&gt;UNB&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;The first problem was a very simple problem where you need to determine whether a car is speeding or not, based on different speed limit zones and the cars speed through that zone.  Everyone solved this one.&lt;br /&gt;&lt;br /&gt;The second problem was a card game simulation called "Beat the Devil".  It's not too devilish (sorry, bad pun), but it requires a bit of input processing and logic. You to read in a deck of cards and simulate the logic of the game. &lt;br /&gt;&lt;br /&gt;The third problem was a geometry problem.  The break down of the problem is that given a bunch of points, you need to find the largest triangle that can be made from the set of points that does not contain any other point.  I give the area formula for a triangle in the problem and a hint about how to easily detect if a point is within a triangle.  To do that, you can form three triangles between the given point and the vertices of the triangle.  If the sum of the area of those three triangles equals the area of the original triangle then the point is within the triangle.  There are other tests, like using dot products to see which side the point is relative to the lines of the triangle or using the general point in polygon test.&lt;br /&gt;&lt;br /&gt;The last problem was the hardest one.  In the problem you have pennies, nickels, and dimes and you want to buy X candies, each of which costs 8 cents. If you put more than 8 cents in, the machine gives you back change using the least number of coins. In the problem, you must buy the X candies using the least number of coins possible.&lt;br /&gt;&lt;br /&gt;Initially, the problem looks easy and it looks like you can solve it by using the largest coins first (greedy algorithm), however, this is not necessarily optimal.&lt;br /&gt;&lt;br /&gt;For example, if we consider the case of wanting to buy 6 candies where we have 3 pennies, 10 nickels, and 0 dimes, then the greedy algorithm gives us the following answer:&lt;br /&gt;&lt;br /&gt;&lt;style type="text/css"&gt;.nobrtable br { display: none }&lt;/style&gt;&lt;br /&gt;&lt;div class="nobrtable"&gt;&lt;br /&gt;&lt;table border="1" cellpadding="0" cellspacing="0"&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;Pennies&lt;/td&gt;&lt;td&gt;Nickels&lt;/td&gt;&lt;td&gt;Dimes&lt;/td&gt;&lt;td&gt;Candies&lt;/td&gt;&lt;td&gt;Coins used&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td align="center"&gt;3&lt;/td&gt;&lt;td align="center"&gt;10&lt;/td&gt;&lt;td align="center"&gt;0&lt;/td&gt;&lt;td align="center"&gt;0&lt;/td&gt;&lt;td align="center"&gt;0&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td align="center"&gt;5&lt;/td&gt;&lt;td align="center"&gt;8&lt;/td&gt;&lt;td align="center"&gt;0&lt;/td&gt;&lt;td align="center"&gt;1&lt;/td&gt;&lt;td align="center"&gt;2&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td align="center"&gt;7&lt;/td&gt;&lt;td align="center"&gt;6&lt;/td&gt;&lt;td align="center"&gt;0&lt;/td&gt;&lt;td align="center"&gt;2&lt;/td&gt;&lt;td align="center"&gt;4&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td align="center"&gt;9&lt;/td&gt;&lt;td align="center"&gt;4&lt;/td&gt;&lt;td align="center"&gt;0&lt;/td&gt;&lt;td align="center"&gt;3&lt;/td&gt;&lt;td align="center"&gt;6&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td align="center"&gt;11&lt;/td&gt;&lt;td align="center"&gt;2&lt;/td&gt;&lt;td align="center"&gt;0&lt;/td&gt;&lt;td align="center"&gt;4&lt;/td&gt;&lt;td align="center"&gt;8&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td align="center"&gt;13&lt;/td&gt;&lt;td align="center"&gt;0&lt;/td&gt;&lt;td align="center"&gt;0&lt;/td&gt;&lt;td align="center"&gt;5&lt;/td&gt;&lt;td align="center"&gt;10&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td align="center"&gt;5&lt;/td&gt;&lt;td align="center"&gt;10&lt;/td&gt;&lt;td align="center"&gt;0&lt;/td&gt;&lt;td align="center"&gt;6&lt;/td&gt;&lt;td align="center"&gt;18&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/table&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;That's 18 coins used.  The optimal only requires 16 coins.  In the optimal, we pay for a candy with 3 pennies and a nickel when we can, that way we save a nickel and don't end up having to use 8 pennies at the end.&lt;br /&gt;&lt;br /&gt;&lt;style type="text/css"&gt;.nobrtable br { display: none }&lt;/style&gt;&lt;br /&gt;&lt;div class="nobrtable"&gt;&lt;br /&gt;&lt;table border="1" cellpadding="0" cellspacing="0"&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;Pennies&lt;/td&gt;&lt;td&gt;Nickels&lt;/td&gt;&lt;td&gt;Dimes&lt;/td&gt;&lt;td&gt;Candies&lt;/td&gt;&lt;td&gt;Coins used&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td align="center"&gt;3&lt;/td&gt;&lt;td align="center"&gt;10&lt;/td&gt;&lt;td align="center"&gt;0&lt;/td&gt;&lt;td align="center"&gt;0&lt;/td&gt;&lt;td align="center"&gt;0&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td align="center"&gt;0&lt;/td&gt;&lt;td align="center"&gt;9&lt;/td&gt;&lt;td align="center"&gt;0&lt;/td&gt;&lt;td align="center"&gt;1&lt;/td&gt;&lt;td align="center"&gt;4&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td align="center"&gt;2&lt;/td&gt;&lt;td align="center"&gt;7&lt;/td&gt;&lt;td align="center"&gt;0&lt;/td&gt;&lt;td align="center"&gt;2&lt;/td&gt;&lt;td align="center"&gt;6&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td align="center"&gt;4&lt;/td&gt;&lt;td align="center"&gt;5&lt;/td&gt;&lt;td align="center"&gt;0&lt;/td&gt;&lt;td align="center"&gt;3&lt;/td&gt;&lt;td align="center"&gt;8&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td align="center"&gt;1&lt;/td&gt;&lt;td align="center"&gt;4&lt;/td&gt;&lt;td align="center"&gt;0&lt;/td&gt;&lt;td align="center"&gt;4&lt;/td&gt;&lt;td align="center"&gt;12&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td align="center"&gt;3&lt;/td&gt;&lt;td align="center"&gt;2&lt;/td&gt;&lt;td align="center"&gt;0&lt;/td&gt;&lt;td align="center"&gt;5&lt;/td&gt;&lt;td align="center"&gt;14&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td align="center"&gt;5&lt;/td&gt;&lt;td align="center"&gt;0&lt;/td&gt;&lt;td align="center"&gt;0&lt;/td&gt;&lt;td align="center"&gt;6&lt;/td&gt;&lt;td align="center"&gt;16&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/table&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;This often happens with greedy algorithms, it turns out they are wrong because the locally optimal decisions made don't necessarily translate to a global optimal.  &lt;br /&gt;&lt;br /&gt;You have to use dynamic programming to solve this problem.  In my solution, I recurse on every way that you can buy a candy using your current coin configuration and store the minimum number of coins used to reach a solution in a table so that I can prevent recalculating answers I calculated earlier.&lt;br /&gt;&lt;br /&gt;This is the essence of dynamic programming or memoization.  You can cut off recursion branches where you've already calculated the solution to a sub-problem.  However, it's not always obvious what the recursion is.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-6807851002286123512?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/6807851002286123512/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/01/practice-sessions-week-3.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/6807851002286123512'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/6807851002286123512'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/01/practice-sessions-week-3.html' title='Practice Sessions - Week 3'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-7550969339634994479</id><published>2009-01-21T10:50:00.000-08:00</published><updated>2009-01-21T17:21:19.672-08:00</updated><title type='text'>TopCoder - SRM 433</title><content type='html'>I competed in SRM 433 on &lt;a href="http://www.topcoder.com/"&gt;TopCoder&lt;/a&gt; this morning.  It didn't go all that well.  I solved the first problem quickly, although I could have been a little faster.&lt;br /&gt;&lt;br /&gt;The problem was to minimize an expression S = A1*B1 + A2*B2 + ... AN*BN, where A and B are vectors of integers and you can re-order A in anyway you choose.  The solution is simple, sort both vectors, one in ascending order and one in descending, and then multiply the two vectors together.  The small numbers from A end up being multiplied by the large numbers of B, which gives you the smallest possible S.  I wasted a bit of time writing an operator to sort in descending order, when I could have just sorted both A and B in ascending and multiplied the values of A starting at 0 through N-1 against B starting at N-1 through to 0.&lt;br /&gt;&lt;br /&gt;When I first read the second problem, I realized that it would probably involve using C++'s next_permutation algorithm from the STL and basically bruteforcing an answer.  However, I wasted a lot of time trying to understand exactly what was asked in the problem.  Frustrated by the problem, I went and looked at the 1000 point problem for a bit.  It looked reasonable, but I abandoned it as I felt with the time left, I should probably try to fix my 500 point problem.&lt;br /&gt;&lt;br /&gt;When I finally got my 500 point solution to come out with the right answers, it was too slow on the larger test cases.  Just as I fixed it to run under the time limit, the contest ended, and I couldn't submit.  If I had have had 30-60 more seconds, I would have had it in.  So, now I have a complete solution sitting on my laptop that I didn't even get to submit.  Very frustrating.&lt;br /&gt;&lt;br /&gt;I think only one person solved all 3 problems from the competition, so I could have been possibly in the top 50 or so if my second problem was submitted.  Unfortunately, I ended up 195th, so my TopCoder standing hardly changed.&lt;br /&gt;&lt;br /&gt;My friend &lt;a href="http://publish.uwo.ca/%7Eogolubit/"&gt;Oleg&lt;/a&gt; competed in Div 1.  The Div 2 500 point problem was the 250 point problem in Div 1.  Oleg had similar difficulties with the problem, so his standing also hardly moved.&lt;br /&gt;&lt;br /&gt;I'm somewhat happy I didn't move into Div 1 right now as I would like a chance to redeem myself in Div 2 before moving on.  My next chance will be Feb. 7th.  Why is it that practice sessions always go better than real competitions? :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-7550969339634994479?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/7550969339634994479/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/01/topcoder-srm-433.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/7550969339634994479'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/7550969339634994479'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/01/topcoder-srm-433.html' title='TopCoder - SRM 433'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5915477284432606062.post-3689415805608758975</id><published>2009-01-20T10:17:00.000-08:00</published><updated>2009-01-23T14:42:19.531-08:00</updated><title type='text'>First post!  Becoming the coach.</title><content type='html'>&lt;style type="text/css"&gt;  &lt;!--   @page { size: 21.59cm 27.94cm; margin: 2cm }   P { margin-bottom: 0.21cm }  --&gt;  &lt;/style&gt;  &lt;br /&gt;Through 2003 to 2005 I participated in the ACM ICPC university programming competitions for the University of New Brunswick.  I made the world finals in both 2003 and 2005.  Participating and training for these competitions are some of my most vivid and fun memories from my undergraduate years.  I learned more about algorithms, data structures, language syntax, attention to detail, and problem solving during these years than I ever did in my classes.&lt;br /&gt;&lt;br /&gt;In the Fall of 2005, I moved from New Brunswick to Victoria, BC to start my PhD and I "retired" from the ACM ICPC programming scene.  However, in Fall 2008 I was approached by some UVic faculty to be involved with starting a team.  The university hadn't had a team in close to 20 years.  I originally promised my wife that my involvement would be minor, as she still clearly remembers my programming obsessed years of 15+ hours of practice a week, but once I started talking about the contests again, it was difficult to stop.  I was quickly sucked back into the world of programming contests.&lt;br /&gt;&lt;br /&gt;I agreed to be the coach for the UVic team and I spent 3-4 weeks trying to get the team up to speed.  In November, 2008, I took them to Vancouver, where they competed in the Pacific Northwest regional, placing 30th out of 82 teams.&lt;br /&gt;&lt;br /&gt;The guys could have easily been in the top 20 with a little luck and more experience, as a simple mistake on their second problem cost them close to 2 hours in debug time.  However, I was extremely proud of their effort and how they came back after that initial set back.&lt;br /&gt;&lt;br /&gt;Following the regional competition, I started a problem of the week challenge as an attempt to recruit other undergraduates that might be interested in participating in the competitions.  Each week I post a new problem.  Based on the difficulty of the problem, a solution is worth different amounts of points.  I keep track of what people solve and I help the students either with solving the problem or debugging their code.  You can check out the problems on our website:  http://www.csc.uvic.ca/content/contest/?q=node/8&lt;br /&gt;&lt;br /&gt;As a consequence of becoming the coach, I also ended up getting back into contest coding and competing myself by participating on &lt;a href="http://www.topcoder.com"&gt;TopCoder&lt;/a&gt;.  I also convinced my good friend &lt;a href="http://publish.uwo.ca/%7Eogolubit/"&gt;Oleg Golubitsky&lt;/a&gt; to compete and I now think he's more obsessed than me :-).&lt;br /&gt;&lt;br /&gt;As another strategy to get students involved, I decided to organize a programming competition for undergraduates in the Spring of 2009.  This contest is set to happen in less than 2 weeks on Jan. 31st.  While at UNB, I ran several such competitions, and they were always a great experience.  Myself and another member of my research lab, &lt;a href="http://ianbull.com/"&gt;Ian Bull&lt;/a&gt;, wrote the problems and Oleg wrote alternative solutions to help us verify our judge's solutions.  We plan to have prizes for best 1&lt;sup&gt;st&lt;/sup&gt;, 2&lt;sup&gt;nd&lt;/sup&gt;, 3&lt;sup&gt;rd&lt;/sup&gt;, and 4&lt;sup&gt;th&lt;/sup&gt; year student along with best overall and perhaps some door prizes.  &lt;br /&gt;&lt;br /&gt;I also started weekly practice sessions and myself plus an instructor (Jason Corless) at UVic recruited students for this by going around to classrooms and telling people about the contests.  We've had two practice sessions so far and they have been going reasonably well.  The first week there were about 15 students and the second week there were 9 or 10.  Many of the students solved one problem the first week and the second week everyone solved at least one.  Last week, I offered a prize to the first person to solve two problems.  Ryan Petty ended up winning a Coffee Crisp for his effort :-).  I'm hoping I can keep the numbers up so that we can send 2-3 teams next year to the regional.   &lt;/p&gt; &lt;p style="margin-bottom: 0cm;"&gt;&lt;br /&gt;This brings my programming competition story up to date.  I'll be continuing to chronicle this experience as well as discuss solutions to various programming problems.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5915477284432606062-3689415805608758975?l=seanfalconer.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://seanfalconer.blogspot.com/feeds/3689415805608758975/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://seanfalconer.blogspot.com/2009/01/first-post-becoming-coach.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/3689415805608758975'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5915477284432606062/posts/default/3689415805608758975'/><link rel='alternate' type='text/html' href='http://seanfalconer.blogspot.com/2009/01/first-post-becoming-coach.html' title='First post!  Becoming the coach.'/><author><name>Sean Falconer</name><uri>http://www.blogger.com/profile/11279850277368681486</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
