One Point Solution

  • Subscribe to our RSS feed.
  • Twitter
  • StumbleUpon
  • Reddit
  • Facebook
  • Digg

Monday, 16 May 2011

std::pair is good

Posted on 06:25 by Unknown
The editorial for the Topcoder Open 2011 qualifcation round 1 is up, but there is something that I don't like about it. To avoid having to explain off-topic STL features, I didn't make the solution for 1000 as simple as it should have been. The culprit is this function:
// Compares two sequences, returns the better one using the tie-breaking
// describing in the statement.
string better( const string& a, const string &b) {
if (a == "...") {
return b;
} else if (b == "..." ) {
return a;
}
if (a.length() < b.length()) {
return a;
}
if (a.length() > b.length() ) {
return b;
}
if (a < b) {
return a;
} else {
return b;
}
}



Innocent enough eh? But isn't it the most repetitive, boring code you will ever have to type? Everything with non-trivial tie breaking rules involves doing something like that. In this problem's case, you need to tie break between "..." , smaller strings and lexicographically-first ones.

For a long time I was skeptical of std::pair and I didn't use it. Neither did I get why the people in the top kept using it. Let us accept it, vector<pair< pair<int,int>, string > > looks almost like obfuscation - Why not use a struct containing named ints and a string?. I eventually learned what was so great about.

  • 1. STL pairs can be created very easily with make_pair.

  • 2. STL pairs implement the < and > operators, and they do it in a rather standard way - First compare the first element, and if the two first elements are equal, use the second for tie breaking. They also implement == and = in a logical way.

  • As a case of great synergy, std::min works when comparing any type that supports = and <.


Where does this take us? Well, for starters it makes pairs very useful for these over-complicated tie breaking days. The following is a way to implement the better() function:

// Compares two sequences, returns the better one using the tie-breaking
// describing in the statement.
string better( const string& a, const string &b) {
if (a == "...") {
return b;
} else if (b == "..." ) {
return a;
}
return std::min(make_pair(a.length(),a), make_pair(b.length(), b) ).second;
}



Explaining: make_pair(a.length(), a) . Will magically make a pair< string::size_t*, string > that has a.length() as first element and a as second element.

* string::size_t is a fancy way to say unsigned int. It causes a lot of issues that things like .length() is declared as unsigned int instead of just a simple int. So many that perhaps this was the main reason Java does not support unsigned.

std::min, will compare both pair< string::size_t, string >s, if the first elements are equal it will compare the second elements. Then it will return the one pair that is the smallest. In other words, if the lengths of the strings are different, it will return the pair with the smaller string, and if they are equal, it will return the pair with the lexicographically-first string. (Because std::string implements < that does lex-first check).


But there is more, what about dealing with "..." ? The main problem with doing that comparison is that "..." has three characters, when it should be the equivalent to the maximum possible result. So, how about we make "..." a pair that first contains a very high number and second "..." ? Then if we use pair<int,string>s to represent possible results, we would just need to return the second element of the best pair we found. Such as the following code:

const int MAX_SEQUENCE_LENGTH = 199;
const int MAX_SQUARE_SIZE = 149;

typedef pair<int,string> result;
const result IMPOSSIBLE = make_pair(2* MAX_SEQUENCE_LENGTH,string("..."));

struct SquareSeries
{
// We use these tables to memoize the results.
bool visited[MAX_SEQUENCE_LENGTH+1][MAX_SQUARE_SIZE+1][26];
result mem[MAX_SEQUENCE_LENGTH+1][MAX_SQUARE_SIZE+1][26];

// constants
string pattern;
int rightStart, lastLength;

//----------
// Returns a sequence that:
// * starts at position p of the pattern.
// * If the square previous to the sequence had size prevsz and
// color prevc, the last square of the sequence
// will have size = lastLength
//
result rec(int p, int prevsz, char prevc) {
//memoize the results:
result & x = mem[p][prevsz][prevc-'A'];
if ( visited[p][prevsz][prevc-'A'] ) {
return x;
}
visited[p][prevsz][prevc - 'A'] = true;

x = IMPOSSIBLE;

if(p == pattern.size()) {
//The end of the pattern. The last size
// should match the one we want.
x = (prevsz == lastLength )? make_pair(0,string("")) : IMPOSSIBLE;
return x;
}

bool alloww = false, allowb = false;
if (pattern[p] != '?') {
//forced to use pattern[p] color
alloww = (pattern[p]=='W');
allowb = ! alloww;
} else { //?
//we can skip to right side:
x = rec(rightStart, prevsz, prevc);
// Allow any of the colors
alloww = allowb = true;
}
for (char ch = 'B'; ch<='W'; ch+='W'-'B') {
// If not allowed to use a color, don't use it
if ( ((ch=='B') && !allowb) || ((ch=='W') && !alloww) ) {
continue;
}
// Change the size according to last color.
int sz = prevsz + ( (ch!=prevc) ? 1: -1 );
if ( sz == 0 || sz > MAX_SQUARE_SIZE ) {
//Skip when size is invalid.
continue;
}
// Continue the recursion for the next character positions
result y = rec(p+1, sz, ch);
x = std::min(x, make_pair(y.first + 1, ch + y.second) );
}

return x;
}
string completeIt(string pattern, int lastLength)
{
// Find the question mark.
int q = 0;
while ( pattern[q] != '?' ) {
q ++;
}
//Split in left and right parts.
string left = pattern.substr(0,q);
string right = pattern.substr(q+1);

//max size of added sequence:
int qw = MAX_SEQUENCE_LENGTH - left.size() - right.size();

//Position at which the right side starts
rightStart = q + qw;

//The new pattern with extra '?' for each possible
//location of a new character.
this->pattern = left + string(qw,'?') + right;

this->lastLength = lastLength;

//Fill visited with 0s.
memset(visited,0,sizeof(visited));

//Starting at position 0, the last square had size 0 and color 'Y'
//the wanted last size is lastLength.
return rec(0, 0,'Y').second;
}


};


pitfalls
make_pair does the type checking automatically. But the problem is that implicit type casts between different std::pairs do not work correctly (or at all). For example:

string y = "hello" ; // This works fine, "hello" is a const char*
// but const char*s can get typecasted as std::strings thanks to constructors.

pair<int, string> p = make_pair(0, "fail"); // This does not work fine.
// make_pair(0, "fail") believes it has to make a pair<int, const char*>
// pair<int, const char*> does not get casted automatically as
// pair<int, string>.

//Possible alternatives:
pair<int,string> q = make_pair<int, string>(0, "fail"); //explicitely use
//the correct make_pair

pair<int,string> r = make_pair(0, (string)"fail");
//cast the const char* to string before passing it to make_pair

pair<int,string> r = make_pair(0, string("fail") );
//a nicer looking way to cast it...



Email ThisBlogThis!Share to XShare to Facebook
Posted in c++, stl, tco, topcoder | No comments
Newer Post Older Post Home

0 comments:

Post a Comment

Subscribe to: Post Comments (Atom)

Popular Posts

  • TopCoder SRM 557 - finally
    SRM 557 Explanation for division 1 Easy and match recap. Explanations for div2 easy and div2 medium. It feels like it has been ages since t...
  • SRM 589 Editorial
    I have finished writing the editorial for TopCoder SRM 589: http://apps.topcoder.com/wiki/display/tc/SRM+589 . As you most likely noticed. L...
  • SRM 590 recap and editorial
    Another week another Topcoder match. Not a great day. I had a bad flu and still do. Div1 500: The one with Xor Given a list of cards with nu...
  • SRM 546: relief
    I figured I should post something about this SRM. I've been very busy these weeks because the semester is ending and I tried to win a t-...
  • SRM 526: The killing wait for results
    While I wait for results, here is my perspective on this algorithm contest. It began with issues, it had to be postponed 15 minutes. TC has ...
  • SRM 554 div1 hard: TheBrickTowerHardDivOne
    Link to problem statement We got infinitely many bricks of dimensions 1x1x1 and C different colors. Count the number of towers of size 2x2...
  • SRM 533: Div1 500 MagicBoard explanation
    Finally solved it. It is a nice problem that is worth explaining in a post. You have a grid/board of at most 50x50 cells. Some cells contain...
  • Member SRM 505: Part 1
    So, let me explain a couple of problems from a Topcoder Member SRM that I wrote and never got an editorial. BTW, it was the last member SRM....
  • ListedLinks 2012-02-10
    Saturday Morning Breakfast Cereal comics: Grace Hopper's ghost That Oracle engineer blog post Oracle would really not like anyone to se...
  • Codeforces "Good bye 2013" round
    So it was a special round for coders of both divisions, problems ranged from the super easy problem A to the super difficult problems E,F,G....

Categories

  • acm
  • algorithm
  • answers
  • arenaplugin
  • badday
  • behindthescenes
  • bugs
  • c++
  • censorship
  • codechef
  • codeforces
  • contests
  • crocchamp
  • editorial
  • editorial.srm
  • embarrassing
  • explanation
  • gcj2013
  • gmp
  • goodday
  • google
  • googlecodejam
  • greed
  • groklaw
  • health
  • html
  • httpseverywhere
  • implementation
  • ipsc
  • ispc
  • java
  • kawigiedit
  • kindagoodday
  • lamebook
  • languages
  • lego
  • listedlinks
  • marathon
  • nasa
  • offtopic
  • ouch
  • postmortem
  • postportem
  • practical
  • probably_not_a_good_tip
  • problemsetting
  • programming
  • python
  • quora
  • rant
  • recap
  • slightlygoodday
  • snippet
  • srm
  • stl
  • strategy
  • swerc
  • tco
  • tco12
  • tco13
  • tco2012
  • tco2013
  • ternarysearch
  • topcoder
  • tricks
  • ubuntu
  • uva
  • vjass
  • vkcup
  • wc3
  • zinc

Blog Archive

  • ►  2014 (1)
    • ►  January (1)
  • ►  2013 (141)
    • ►  December (14)
    • ►  November (8)
    • ►  October (13)
    • ►  September (11)
    • ►  August (14)
    • ►  July (15)
    • ►  June (13)
    • ►  May (13)
    • ►  April (12)
    • ►  March (11)
    • ►  February (11)
    • ►  January (6)
  • ►  2012 (94)
    • ►  December (5)
    • ►  October (6)
    • ►  September (8)
    • ►  August (6)
    • ►  July (3)
    • ►  June (5)
    • ►  May (8)
    • ►  April (10)
    • ►  March (20)
    • ►  February (16)
    • ►  January (7)
  • ▼  2011 (51)
    • ►  December (7)
    • ►  November (12)
    • ►  October (5)
    • ►  September (1)
    • ►  August (3)
    • ►  July (4)
    • ►  June (3)
    • ▼  May (7)
      • Thoughts after SRM 507
      • Thoughts after GCJ 2011 round 1
      • 2010's gcj prediction about 2012, how is it going?
      • std::pair is good
      • Bugs that come back to haunt you.
      • Some thoughts after SRM 506.
      • Google code jam qualification round
    • ►  April (3)
    • ►  March (2)
    • ►  February (1)
    • ►  January (3)
  • ►  2010 (9)
    • ►  December (4)
    • ►  October (1)
    • ►  June (1)
    • ►  May (1)
    • ►  January (2)
  • ►  2009 (1)
    • ►  December (1)
Powered by Blogger.

About Me

Unknown
View my complete profile