Sunday 9 May 2010

Google Code Jam 2010 - Qualification round

Qualification round of Google Code Jam is over, I have managed to get only 53 points of 99 possible. Not a very good start, but nevertheless I proceeded to Round 1. Here is some of my code, that I solved the problems with.

Snapper Chain

It was the easiest problem to solve. (Problem Text) The key to the solution is that the chain shows number of snaps in binary (actually last n bits of it), and light is on when all snappers are in ON state (= the last n bits are all equal to 1).

#include <iostream>
#include <math.h>

using namespace std;

int main(){
  unsigned int t, n, k;
  cin >> t;
  for (unsigned int i=1; i<=t; i++){
    cin >> n >> k;
    unsigned int d = (1U << n ) - 1;

    cout << "Case #" << i << ": " <<
          (  (k & d) == d ? "ON" : "OFF" ) << endl;
  }
}

Provided code was successful with large input too.

Fair Warning

This one was a little bit harder. (Problem Text) The key is that the difference is time between any 2 events stays the same, no matter how big/small number of starboseconds we add to all of them. So we need a greatest common divisor of the differences.

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

typedef unsigned int uint;

uint gcd( uint a, uint b ){
  if ( b == 0 ) return a;
  return gcd( b, a % b );
}

uint op_minus( uint a, uint b){
  return a > b ? a - b : b - a;
}

int main(){
  uint c, n, tmp;
  cin >> c;
  for (uint i=1; i<=c; i++){
    cin >> n;
    vector<uint> t;
    for (uint j=0; j<n; j++){
      cin >> tmp;
      t.push_back( tmp );
    }
    //after first transformation t[i] will have the difference 
    //between former t[i] and t[i+1]. 
    //The last member t[n-1] remains - it will be needed at the end 
    //to find the required amount of seconds to add 
    transform( t.begin(), t.end()-1, t.begin()+1, t.begin(), op_minus );
    //after second transformation t[i] will have the gcd of t[i-1] and t[i]
    //I use the following feature of gcd: gcd(a,b,c) == gcd(gcd(a,b), c)
    //every time we are at t[i] there is alreaady a gcd(t[i-2], t[i-1]) in t[i-1]
    transform( t.begin(), t.end()-2, t.begin()+1, t.begin()+1, gcd );
    
    uint remainder =  t.back() % *(t.end()-2);
    cout << "Case #" << i << ": " <<
            (remainder > 0 ?  *(t.end()-2) - remainder : 0 )<< endl;
  }
}

This code does not solve the large input as there are integers up to 1050, and we need to use a bigint class, which I didn't find/write myself.

Theme Park

This is the hardest one to solve, because small input can be solved with straight approach, but the large one needs some different. And I couldn't figure it out. (Problem Text) The code that solves the small input is pretty straightforward. We take the group at the beginning of the queue as soon as it can fit in roller coaster. And don't forget that 1 and the same group cannot be taken twice in 1 run (that may happen if number of seats in roller coaster is more than all the number of people in queue)

#include <iostream>
#include <vector>

using namespace std;

typedef unsigned int uint;

int main(){
  uint t, r, k, n, tmp, sum, total;
  cin >> t;
  for (uint i=1; i<=t; i++){
    cin >> r >> k >> n;
    vector<uint> g;
    total = 0;
    for (uint j=0; j<n; j++){
      cin >> tmp;
      g.push_back( tmp );
    }
    uint a=0;
    for (uint j=0; j<r; j++){
      uint sum = 0;
      for (uint b=0;  b<n && sum + g[a] <= k; a=(a+1)%n, b++ ){
        sum += g[a];
      }
      //cout << "sum: " << sum << " ";
      total += sum;
    }
    cout << "Case #" << i << ": " << total << endl;
  }
}

Pity, the same approach is too slow for large input.

The "official" solutions can be found here.

3 comments:

  1. Saying Hello also from Estonia ;)

    I to took part from CodeJam2010 and most difficult task for me was second. Not because it was really difficult but because of some stupid defect. My solution solved the small one and also solved the large one but because the large integer library what I used for that was faulty the one result was also messed up. Bad luck I say
    Anyway what I wanted to say was to describe how I solved third one. It was quite clear that even my quite fast computer could not brute force the large input so I did it another way. Because of a such small number of possible groups I thought that it will start to loop it self really fast. So, if we use example data, only increase R to(for example) 10000 then we get that
    group 1, 4, 2, 1 will start to sit in train like that(!marked group are sitting in train)

    ride 1: !1 !4 2 1 cost 5 Total 5
    ride 2: !1 4 !2 !1 cost 4 Total 9
    ride 3: 1 !4 !2 1 cost 6 Total 15
    ride 4: !1 !4 2 !1 cost 6 Total 21
    ride 5: !1 4 !2 !1 cost 6 Total 27( We already have been here in ride 2 )

    So actualy the rollercosting will be like that:
    ride 1:
    while(there is still rides left){ rides 2,3 and 4 }

    Of course it will not be exactly like that( loop and ride count don't have to end together. Rides count can end in last loop ). But main calculations to solve it is like that:
    --------------
    Variables we have
    rides=how many rides has been by the moment we discovered first loop
    money=how much money was spent by the moment we discovered first loop
    sinceRide=how much rides was done by the time we last were in the loop starting point
    sinceMoney=how much money was gathered by the time we last were in the loop starting point
    -------------------------
    Calculations
    tmpRides=rides-sinceRide
    tmpMoney=money-sinceMoney
    loops=(R-rides)/tmpRides

    money+=loops*tmpMoney
    rides=loops*tmpRides
    -------------------------
    By this moment we are almost in the end for the rides. Ride count is really really close to the R and we can brute force rest of the way.

    Later I also add the source code but this was the logic behind it. Also, because I didn't bother to calculate largest possible values, I used __int64 for calculations. I was not sure that 32 bits are enough.

    The solution was fast enough and entire input was solved in 0.3 seconds. And even better, it was correct :)

    ReplyDelete
  2. So, and the promised source code( I used Visual Studio because I was at work ):
    #include "stdafx.h"

    #include







    int _tmain(int argc, _TCHAR* argv[])

    {

    FILE *fin,*fout;

    __int64 T,R,k,N,pos,rides,size,money,started;

    __int64 g[1001];

    __int64 sinceVal[1001],sinceRid[1001];

    fin=fopen("c:\\test.in","r");

    fout=fopen("c:\\test.out","w");

    fscanf(fin,"%I64d",&T);



    for(__int64 h=0;h1 ? 1 : 0);

    rides=0;

    money=0;

    started=0;

    for(__int64 x=0;x k || pos==started){

    money+=size;

    rides++;

    if(sinceRid[pos]>0){

    __int64 vaheRides=rides-sinceRid[pos];

    __int64 vaheMoney=money-sinceVal[pos];

    __int64 loops=(R-rides)/vaheRides;

    money+=loops*vaheMoney;

    rides+=loops*vaheRides;

    }

    sinceVal[pos]=money;

    sinceRid[pos]=rides;

    size=0;

    started=pos;

    }



    size+=g[pos];

    pos++;

    if(pos == N)

    pos=0;

    }



    fprintf(fout,"Case #%I64d: %I64d\n",h+1,money);

    }

    fclose(fin);

    fclose(fout);

    }

    ReplyDelete
  3. Thank you! I will investigate your algorithm and code :)
    Very nice of you to share your thoughts. You have the honour of being the author of the first great comment on my blog! Congratulations! :)

    ReplyDelete