Tuesday, March 25, 2014

Find The Largest Number of Turns - C++

おさしぶり C++. It's been a while C++.

I was given a card game problem where I had to find the max number of turns given a vector of cards.

The rules were:
-Dmitry chooses an arbitrary card from the pile. Let A be the number written on this card.

-The card with number A is removed from the pile.

-If the pile contains a card with number A-1, that card is removed from the pile.

-If the pile contains a card with number A+1, that card is removed from the pile.

The constraints (which made things a LOT easier) were:
-cards will contain between 1 and 50 elements, inclusive.

-Each element of cards will be between 1 and 499, inclusive.

-All elements of cards will be distinct.

-Cards may or may not be in order

I approached this with a greedy approach to try to maximize the amount of turns and to do so every turn we have to remove the least amount of cards.

Then I noticed that if you started from the ends, you will at most remove a maximum of 2 cards.

Also in the case where two cards are taken out, we can't maximize any better than that since if either one were elected to be removed, the other would also be removed.

Then I noticed that the constraint was that each card has a value from 1-499. So iterating from 1 to 499 will create that algorithm where we go from one end to the other.

Next comes with checking if the vector contains the A+1 card. Instead of scanning the vector each time, which will cost O(n^2) I first put the values into an unordered_set since find() is a constant time algorithm on average. Another option is to do a sort on the list but that would bump the complexity to O(nlogn).

So with all that said here's the algorithm:


class SRMCards
{
 public:
  int maxTurns(vector<int> cards);
};

int SRMCards::maxTurns(vector<int> cards)
{
 int turns = 0;
 unordered_set<int> cardSet;
 unordered_set<int>::iterator pos1;
 unordered_set<int>::iterator pos2;

 // Initialize our set with the card numbers
 for(vector<int>::iterator it = cards.begin(); it != cards.end(); ++it)
 {
  cardSet.insert(*it);
 }
 
 // For every  possible card number starting from the lowest to the highest value
 // check if the value and value + 1 is in the set. If it is remove both, otherwise 
 // remove value.
 for(int i = 1; i < 500; i++)
 {
  if(cardSet.size())
  {
   if((pos1 = cardSet.find(i)) != cardSet.end() &&
    (pos2 = cardSet.find(i + 1)) != cardSet.end())
   {
    cardSet.erase(pos1);
    cardSet.erase(pos2);
    turns++;
   }
   else if((pos1 = cardSet.find(i)) != cardSet.end())
   {
    cardSet.erase(pos1);
    turns++;
   }
  }
  else break;
 }

 return turns;
}

No comments:

Post a Comment