Showing posts with label badday. Show all posts
Showing posts with label badday. Show all posts

Saturday, April 11, 2015

Google Codejam 2015 Qualification Round : Part 2: A small/large

[Part 1]

My compiler

The other language I initially intended to use in codejam was an old compiler thing I coded in 2013. It was a school assignment that took me a couple of days to implement. It's just a C clone that doesn't even have arrays. But hey, at least it has integers. And can actually read. Although it can only read one integer, and write one integer. It doesn't have division either. But it compiles to 64 bits asm code which can be compiled to binary, so speed isn't a big issue.

Problem A - The one with shyness - Standing Ovation

I had to pick a good problem for this. But all seemed unsuitable. My first idea for problem A - Standing OvationWas to just bruteforce for the number of additional members. There's no reason to bring audience members who are shy, so their shyness should be 0. The maximum result is equal to Smax. For each candidate number of additional members, we can just simulate the process and check if everyone claps. So we can do this in an `O(S_(max) N)` loop quite easily.

For the large version, and also to solve using a language that has no arrays. We can use an on-line algorithm. We initially have `x` audience members with 0 shyness. We know those will be clapping. So we should worry about the rest. We encounter that there are `s_1` audience members with shyness 1. If `1 > x`, we need to invite one more audience member: `1 - x`. In fact, we can repeat this, now we know there are `max(1,x) + s_1` audience members that are clapping. We find that there are `s_2` members with shyness 2. And so and so.

The problem was that I needed to have a way to turn the neat input file into something my language can read. Then turn the language's output into something that looks like "Case #1: xxx". DISASTER, I actually learned that my asm code only does syscalls for reading bytes and then converts to integer and is not quite the same as a scanf or cin>>x... In fact, the whole thing really fails at reading files. Even ignored the format issues. I had to learn how to automatize having a terminal send keystrokes to my program for it to work. Apparently this is usually a thing that people need as there is a family of programs called expect that do this. Fortunately, after learning how to use expect I found that python has a pexpect module. This allowed me to create this nifty glue python program:

import pexpect
import sys

# call the solver program, return the result
def call_solver(smax, shy):
    try:
        child = pexpect.spawn ('./A')
        child.sendline('%d' % smax)
        child.expect( '.*' )
        for ch in shy:
            child.sendline ('%s' % ch)
            child.expect( '.*' )
        child.expect(pexpect.EOF)
        return int(child.before.split('\r\n')[-2])
    except:
        print "ERROR"
        return -1


T = int(raw_input() )
for i in range(1, T+1):
    s = raw_input()
    (smax, shy) = s.split(' ')
    smax = int(smax)
    print "Case #%d: %d" % (i, call_solver(smax, shy) )  
    sys.stdout.flush()

So now I am free to make my program using my language...

program Codejam2015A
{
    int n, a, s, i, cost;

    read n;
    read s;
    
    i = 1;
    cost = 0;
    //initial clappers (shyness = 0)
    while (i <= n) {
        read a;
        if (a >= 1) {
            if (i > s) {
                // must invite i - s
                cost = cost + (i - s);
                s = i;
            }
        }
        s += a;
        i = i + 1;
    }
    print cost;
    
    
}

Anyway, you can find the complete source code in the codejam score boards...

I submitted A-small, had a failure because it was slower than I thought, I tried to check if there was something wrong. It seems expect is slow in sending things.

I should have known that the reason it was slow would only get worse in A-Large, in fact, when I tried A.large, it turned out that it could barely solve one case per minute, even tho the program was, the glue stuff using expect was very slow, specially when there are 100000 line enters to send to the fake terminal... Failed A-large because of a time out.

Tuesday, August 12, 2014

SRM 629: A mistake

I think reading the problem statement is half the batte. Or actually, understanding the problem statement is half the battle.

Div1 250: The one with a rectangular hole

You have a rectangular hole of some possibly large dimensions. Also some boards that you can use to cover the rectangle. The boards are rectangles themselves. They may be rotated and overlap. But all of their corners must lie strictly outside the whole. Also the sides of the boards must be paralel to the sides of the hole. I put emphasis in all because I spent most of the match with the ridiculous assumption that only one of the corners must be outside the hole. This assumption makes no sense to be made because the statement is quite clear. I have no idea what happened, but this other variation of the problem seems very difficult, might even be impossible with the given constraints :/. Also note that the corners do not need to be put in integral coordinates, the statement doesn't mention this explicitly but an example needs this. Of course, you return the minimum number of boards needed to cover the rectangle. There are up to 50 boards.

So the trick to this problem is to notice that with the corner constraint, then all boards must be put in sequence either horizontally or vertically . Overlaps are not helpful. With this knowledge, we can first try horizontally and then vertically. As easy as swapping the dimensions of the rectangle, so we first try with W = holeW, H = holeH and then with H = holeW, W = holeH. The idea is that each board's height must be strictly larger than the hole's height (H). Then the board will cover a part of the width. Just find the top X possible widths, if those widths can cover the whole width W, then you have a result `t`. Note that when choosing each board's width and height we need to pick a good rotation...

import itertools

INF = 10**9

def minimumNumberInf(holeH, holeW, boardH, boardW):
    res = INF
    for (W,H) in ( (holeH,holeW), (holeW,holeH) ):
        # all vertical
        def correctWidths():
            for (wr,hr) in zip(boardH, boardW):
                best = -1
                for  (w,h) in ( (wr,hr), (hr,wr) ):
                    if h > H:
                        best = max(best, w)
                yield best
    
        widths = sorted( [ w for w in correctWidths() if w != -1 ] )
        
        if sum(widths) >= W:
            r = next( t for t in itertools.count(1) if sum(widths[-t:]) >= W )
            res = min(res, r ) 
    return res


class RectangleCovering:
 def minimumNumber(self, holeH, holeW, boardH, boardW):
    r = minimumNumberInf(holeH, holeW, boardH, boardW)
    return -1 if r >= INF else r

Because of the interpretation mistake, I took way too long to solve this problem. I also started the match a bit late so I didn't have time to read div1 500 either :/

Tuesday, May 20, 2014

SRM 621: Invisible slowness

So another SRM day. I've been with headache since yesterday so running at half the steam...

Div1 275: The one with the geometry

So you have a circle (signal) centered at `(0,0)` with radius `S` , `S` is a real number picked uniformly at random from 0.00 to `Z`. There are some other circles (cities) each with center `(x,y)` and radius `r`. Things are good if, for each city, the main circle (signal) either doesn't include the city or it includes ALL of the points inside the city. Return the probability that this happens. The answer can have an absolute or relative error or 1e-6 (whichever is smallest)

So that's a nice thing about the allowed error. After topcoder added the ability to include a custom checker in problem sets, I thought it was only a matter of time until someone makes a problem that allows less precision than the usual 1e-9. The first thing I did in this problem was take my special python tester template that allows you to test automatically even when there is a custom checker. I used CNP super powers to paste the usual python checker, but replacing the 1e-9 with 1e-6... I really wanted to have accurate tester results.

The first thing is to notice what makes a radius `S` valid for a specific circle with `(x,y,r)`. A nice trick in problems involving plenty of circles is to consider only the distance between their centers. Eventually you will notice that if the distance between `(0,0)` is `d`, then there are two ranges in which is `S` is valid: `S <= d - r` or `S >= d + r`. Something that caused me to take a bit of time in this sub-problem, because of a special corner case: What if `(0,0)` is inside circle `(x,y,r)` ? Then `d-r` is negative. It turns out that it doesn't matter. `S` cannot be negative, and thus: `S <= d - r` or `S >= d + r` is still a good condition.

Once we know that condition. How to deal with the probability? You should see those `d-r` and `d+r` (for all cities) as the only points where the validity of `S` could change. So we can divide the `[0,Z]` interval using those points as delimiters. Now we can test each segment `[a,b]`, we know that `a` or `b` are points in which the result could change, so all points exclusively inside `(a,b)` will have the same result. `S` is valid for `(a+b)/2` if and only if it is valid for all points in interval `[a,b]`. For each valid interval `[a,b]`, if any of those points is picked for `S`, it is valid just add `(b - a) / Z` to the probability.

class RadioRange:
 def RadiusProbability(self, X, Y, R, Z):
    cities = zip(X,Y,R)
    
    def goodCity(x,y,r,s):     #city completely inside circle of radius s or not
        d = (x**2 + y**2) ** 0.5
        return ( (s <= d - r) or (s >= d + r) )
    
    def goodRadius(s):         # each circle must be completely included or not
        return all( goodCity(x,y,r,s) for (x,y,r) in cities )
    
    def getEvents():
        yield 0.0
        yield Z
        for (x,y,r) in cities:
            d = (x**2 + y**2) ** 0.5
            yield d + r
            yield d - r
    
    # remove invalid events (less than 0, more than Z), duplicates (using set)
    # and turn into a list (so we can use indexes) and sort
    # (possibly unnecessary to call sorted, the set likely already sorted)
    evs = sorted( list(set( x for x in getEvents() if (0 <= x <= Z) ) ) )
    good = 0.0
    for i in range(0, len(evs) - 1):
        p = (evs[i] + evs[i+1]) / 2
        if goodRadius(p):
            good += evs[i+1] - evs[i]
            
    return good / Z

I knew how to solve the problem since the beginning, except dealing with the corner case that turned out not to exist anyway. I am not sure why it took me so long to solve it, but it did.

Div1 500: tree stuff

Seems hard, I was tired and with headache so I went to lie down while I supposedly thought of the problem, but couldn't think of much.

Challenge phase

During the challenge phase I discovered why people were faster than my in 275, it turns out that `S` is invalid if and only if it intersects with any circle (city) in the input. While most of the code remains the same, this makes reaching the code a bit easier and also removes the need for checking for validity of `(a+b)/2`at the end, instead you just do a line sweep. (Each event `d-r` increments the number of intersecting circles, each `d+r` decrements it, sum up intervals in which the count is 0.)

Tuesday, March 04, 2014

SRM 611: My first python SRM

The other day, I made the foolish decision to use Python exclusive in SRMs 611 to 620. I suspected it will be bad for my rating at least initially. I am not as experienced with python as I am with c++ and there is always a good chance the admins will include a problem in the set that is unsolvable in python. On the other hand, maybe the 250 has a quick to think solution and then python's conciseness would help me score some points.

Div1 250: The one with LCM

You are given two sets `A`, `B`, the LCM set of a set `S` is the set of all numbers `L` such that `L` is equal to the LCM of the numbers in a subset of `S`. Given `A` and `B` find if their LCM sets are equal. Numbers in each set are between `2` and `10^9`, inclusive.

Tough luck, it didn't appear this was going to be a quick problem to code, and at first I had no idea what to do.

Eventually, I thought of something that appears to be a solution. Given a set `X`, we can get rid of any number that can be created by taking the LCM of other numbers in the set. After doing this, set `X` becomes the minimal set such that its LCM set will be equal to `X`. If we find the minimal sets of `A` and `B`, they will be equal if and only if the LCM sets are equal.

Implementing was another issue. How can you tell if a number can be made from the other numbers in the set? Well, you would need to process the numbers in decreasing order. I focused on the prime decomposition of each number, if the exponent of at least one prime number is larger than the maximum ever exponent in the numbers smaller than it, then this number shouldn't be removed. I needed to code Eratosthenes sieve in python for the first time, it probably isn't so well coded.

I made a mistake, the first implementation would ignore the smallest number in each of the sets, so it thought `{7}` had the same LCM set as `{8}`. The slow submission plus the resubmit made me score incredibly low. I am not 100% sure about this approach, but if I fail system tests it will not make a difference, my ranking in the match will be very low either way.

import math

class LCMSet:
 def equal(self, A, B):

    # greatest common divisor
    def gcd(x,y):
        while y != 0:
            (x,y) = (y, x%y)
        return x
    
    # least common multiple:
    def lcm(x,y):
        return x * y / gcd(x,y)
    
    # Eratosthenes siege returns an iterator of prime numbers, we don't need
    # primes larger than sqrt(max( max(A), max(B) ))
    def sieve():
        t = int( math.sqrt(max(max(A), max(B))) + 1 ) 
        compos = [ False ] * (t + 1)
        for i in xrange(2, t + 1):
            if not compos[i]:
                yield i
                j = i + i
                while j <= t:
                    compos[j] = True
                    j += i
    
    # make a list of prime numbers
    primes = list( sieve() )
    
    # prime decomposition:
    def primeDecomp(x):
        d = dict()
        for p in primes:
            c = 0
            while x % p == 0:
                x /= p
                c += 1
            if c > 0:
                d[p] = c
        if x > 1:
            d[x] = 1
        return d

    # "compress" a set, find the minimal set that has a LCM set equal to X
    def compress(X):
        decomp = [ primeDecomp(x) for x in reversed(X) ]
        n = len(X)
        c = []
        for i in xrange(n - 1):
            good = False
            # for each p^r in the prime decomposition:
            for (p , r) in decomp[i].items():
                if r > max( 0 if p not in decomp[j] else decomp[j][p] for j in xrange(i+1,n) ):
                    good = True
            if good:
                c.append( decomp[i] )
        #smallest number is always there. Missing this line caused the resubmit
        c.append( decomp[n-1] )
        return c
        
    return ['Equal','Not equal'][compress(A) != compress(B)]

Div1 500: The one with spanning tree

You are given a complete graph of up to 20 vertices with weighted edges (It is given in the form of points in a map that you have to connect using a tree topology). Return the minimum standard deviation of the edges of a spanning tree.

Easy, right? Well, the `20` constraint hints me that some heavy dp or brute force is needed. I remembered a match in which we had some sort of brute force/dp through all spanning trees, but I didn't remember enough. Also, the constraint wasn't a good sign for whether the problem is solvable in python or not :/

The end

Plenty of very different solutions for 250. I have no idea if I will pass, I expect many system test fails.

What about python? Well, this wasn't a great match for comparisons. I think I would have taken very long to code a solution in c++ too. I am currently sleep deprived, I stayed up till very late last night trying to finish something that barely passes as an editorial for SRM 610. I guess there is a good reason I am doing this for 10 SRMs and not just a couple.

Update: Failed system tests

What a bad day. Div1 250 approach fails the following test case: [{168, 7476, 84, 890, 10, 4, 40, 3560, 37380, 8, 89, 20}, {84, 4, 20, 7476, 3560, 89, 712, 420, 74760, 1780, 14952, 8, 356, 10}], it is a wrong answer so this is likely a mistake with the algorithm and not python's fault.

Monday, December 09, 2013

SRM 599 Recap and editorial : Cursed

I just barely finished SRM 599's editorial. After failing this match both as contestant and editorial writer. My conclusion is that this match has been cursed.

Div1 250

(Read editorial for actual explanation)

I was so happy with this problem when I first read it and... horribly misinterpreted it. For some stupid reason, I thought that when doing the second kind of step, you would always raise the number to square. I am not sure why this assumption happened, or how I managed to misread the problem. But it happened. The reason I liked this, is that with this assumption, the problem reduces to a div1 250 we already solved very recently. So I thought that sort of thing was cool.

I coded my solution, it passed example tests (should so confused solutions pass example tests? Maybe examples were weak?), moved on to div1 500.

Challenge phase and destruction

For a second I thought of not participating in the challenge phase. I was sort of busy that day. But last minute I decided [[It is likely people are making all sorts of bugs in 250 and bad assumptions in 500]]. I was right. Unfortunately, the first solution I opened was correct, but I didn't know it was correct (remember I misunderstood the problem entirely). So I challenge it. The challenge failed and the [Correct answer] provided by the system didn't make any sense to me. That's when I figured that a) My 250 was utterly wrong. b) I had a bad challenge, so this means -25 overall points. Worst position possible, worst rating loss possible. My only hope was to find codes that were wrong and challenge them and recover frm the -25. And to do it fast, because if someone challenged by 250, I would not be able to make any challenges any more. I tried a solution, my challenge was also wrong. Gave up...

Div1 500

What a hellish problem from hell!

During the match, I was able to do just about most of the theory. You can only use integer sides. It is impossible for odd cases. Even cases can be solved with 4 sides. You need bruteforce to find out if a triangle is possible. Unfortunately the time I had allocated during the match was not nearly enough for me to code a solution using this idea and optimize it. OMG, optimizing this brute force was so difficult.

I think this problem would have been a good div1 medium, if it only had the theory part. Such complex implementation on top of the theory makes it too extreme for this slot, imho. The constraints were certainly too tight. I think L <= 1000 or 2000 would have been just fine.

I had so many issues solving and explaining this problem. I had to spend ages coming up not only for optimizations, but for optimizations that I can prove are correct. Eventually it was clear I was not going to finish in less than 48 hours if I wanted to solve this problem. So I moved to the hard... Bad idea.

Div1 950

I didn't open this problem during the match. At first the description sounded very easy. rng_58 called it "an implementation problem". Well yes, but not that much. I didn't really understand how the recurrence worked, and I had to do plenty of reverse engineering of rng_58's code. ... If only Petr made his blog linking to author explaining it in codeforces before, maybe it would have been better. ... (BTW, why?, why can't we have TC problem set writers explaining that way in TC's forums? I mean, how did TopCoder manage to kill its community so much? The forums used to be so active and interesting before)

Finishing the editorial

When I noticed, I was already late for both deadlines. And I was finally understanding how to do the hard. Then I had to fix again what I had for div1 medium. Then do the other problems, which were not non-trivial to explain at all. There was a time period in which I thought I would never finish this editorial.

If you think editorial delays are bad for you, editorial reader, think about how awful they are for the editorial writer. Instead of spending a couple of days in a SRM, I ended up spending almost a week on it. There's plentyof things I couldn't do because of it.

Comments and please vote

Any comments. Also please post feedback to the editorial page, specially, vote. I really dislike that there are so few votes (negative or positive) in editorials when SRMs have hundreds of participants. (Did I already mention something about TopCoder's community being dead?)

The problem set was cool, although 500 was probably too interesting for div1 medium. Maybe swapping div1 500 and div1 hard and making div1 hard have smaller constraints would have been better? I really dislike to have failed this match, I really could have done more.

Saturday, October 05, 2013

SRM 593: Meh

The one with hexagon grid

You are asked to color some hexagons in an hexagon grid using the least number of colors in such a way that no two adjacent hexagons have the same color.

I messed up during the match, first I thought that the maximum number of colors to color it was 4. It turns out it was 3, which I found out too late. Under the assumption it was 4 I wasted some precious minutes. What is really sad is that I actually looked at this page: http://en.wikipedia.org/wiki/Hexagonal_tiling#Uniform_colorings, in the first minute after opening the problem and I still didn't notice.

Once I knew the maximum result was 3, then you have to verify if it is possible to color using 0, 1, or 2 colors.

I made the assumption that this depended only on the degree, which was wrong. If there are no hexagons to color, the result is zero. If the maximum degree (maximum number of must-paint hexagons adjacent to another must-paint hexagon) is 1 or 2, then we can paint them using 2 colors (because they form a collar or a line). My mistake was in assuming that degree = 3 meant that you needed 3 colors. There is a specific case for which this isn't true:

-X-
-XX
-X-

Too bad I didn't find it, I actually tried a few hexagons with degree 3 and convinced myself that the adjacent hexagons would always share an edge. If I didn't make this blunder, I would have just done a bipartite check. If the graph is bipartite, then you can paint using 2 colors. That's the solution.

The one with teams

This one was 450 but I really couldn't break its secret. I was trying a dynamic programming, but it wasn't working at all. Then the match ended.

Challenge phase

I knew that my 250 was slow, but I was also pretty sure it was tricky (I even prepared some cases, because there were no example cases in which the maximum degree was 1, for example). But I didn't have that much luck. The codes were very complicated to read. I did find one solution that wasn't even relying on degrees but on partial degrees. So I challenged it, and was happy, because the 50 pts would fix the slow submission. So I went to see the room summary and boom: My solution was already challenged. Probably if I noticed about my solution getting challenged I wouldn't have risked making that challenge...

Comments?

I liked the 250 until I learned checking for bipartiteness was mandatory. Too messy for the slot. 450 said "difference" when it should have specified absolute value of the difference, this made me waste time coding a wrong solution.

Monday, August 12, 2013

SRM 588: ouch (Update)

As I write this, I have to go, I am scheduling this to be up the time the challenge phase ends.

Div1 medium: The one with keys

There are up to 12 rooms, each with some red and green locks (up to 10 of each kind). You can use red or white keys to open red locks and green or white keys to open green locks. Each key is consumed once you use it. Each room has keys inside. What is the maximum number of keys you can have?

I was at first trying to make a viable dynamic programming solution. Similar to a GCJ problem. But I was not able to optimize it.

I eventually settled for a brute force search. Whenever you decide to open a room, you should only use white keys if you NEED to. This approach was slow, so I optimized it using all the dirtiest tricks in the book. Let us see if it passes.

Update: Turns out it passes system tests, so I will explain it.

Try all permutations of the order in which you open the rooms.

If you decide to enter a room and you have enough red and green keys, then you simply shouldn't use white keys. You should always use as little white keys as possible. Turns out this is also the "key" to solve it using dynamic programming.

With this, your approach needs `O(n!)` time, 12! is high, but not very high. So we can try some optimizations.

Imagine that at a given moment, there is one room that can be opened and after opening it, you end up with at least as many keys of each type as before. Then you should always open this room. So you shouldn't try sequences in which this room isn't opened.

Another special case, there is a room that you can open, but after opening it you end up with less keys of each type. This room is never a good idea. Ignore it.

I think that the two previous optimizations are enough , I was about to submit this, but last second I decided I could do an extra optimization, give priority to moves that give the maximum number of total keys, and cut the search when we tried too many options. I am not sure this is necessary, but I put it just in case.. Update 2: Yeah, it turns out this optimization wasn't needed.

Before the match I found out std::function causes a bug in TopCoder. That was too bad, because since there is no sane way to have anonymous recursion in c++ without using it, I had to use an outside function for the backtracking. This meant I had to copy all 5 argument variables as class members. What a bore.

vector<int> doorR,doorG,roomR,roomG,roomW;
int n, best, options[12];

void rec(int p, int r, int g, int w)
{
    best = std::max(best, r + g + w);

    if (p < n) {
        //each tuple is (i, nr,ng,nb):
        tuple<int,int,int,int> results[n-p]; //possible options
        int t = 0;
        
        for (int i=p; i<n; i++) {
            // for each candidate options[i], find if it is possible,
            // save it in results
            int &x = options[i];
            int nr = r - doorR[x];
            int ng = g - doorG[x];
            int nw = w;
            if (nr < 0) {
                // Not enough red keys, use white keys
                nw += nr;
                nr = 0;
            }
            if (ng < 0) {
                // Not enough green keys, use white keys
                nw += ng;
                ng = 0;
            }
            if (nw >= 0) {
                // if the number of white keys is non-negative we can do it
                // Increase the number of keys
                nr += roomR[x];
                ng += roomG[x];
                nw += roomW[x];
                if ( nr >= r && ng >= g && nw >= w) {
                    // This move is always a good idea, we should do it:
                    swap(options[p], options[i]);
                    rec(p+1, nr,ng,nw);
                    t = 0;
                    swap(options[p], options[i]);
                    break;
                } else if ( nr > r || ng > g || nw > w) {
                    // Make sure the move is a good idea before adding it
                    results[t++] = make_tuple(i,nr,ng,nw);
                }
            }
        }
        // process tuples:
        for (int j=0; j < t; j++) {
            int i, nr,ng,nw;
            tie(i, nr,ng,nw) = results[j];
            swap(options[p], options[i]);
            rec(p + 1, nr, ng, nw);
            swap(options[i], options[p]);
        }
    } 
}

int maxKeys(vector <int> doorR, vector <int> doorG, vector <int> roomR, vector <int> roomG, vector <int> roomW, vector <int> keys)
{
    // copy stuff, this is tedious:
    this->n = doorR.size();
    this->doorR = doorR;
    this->doorG = doorG;
    this->roomR = roomR;
    this->roomG = roomG;
    this->roomW = roomW;
    best = 0;
    for (int i=0; i<n; i++) {
        options[i] = i;
    }
    rec(0, keys[0], keys[1], keys[2]);
    return best;
}

Div1 easy: The one with songs

You have T time available to play as many songs as you can. Each song has a duration and a tone. If the tones of two consecutive songs are `x, y` then you need to rest `abs(x-y)` time units before playing the second song. What is the maximum number of songs?

It took me too long to correctly code this solution. As usual, I waited till there were less than 10 minutes before opening this problem.

The trick is, let us say you decide to play some songs. If the minimum tone is `p` and the maximum tone of the songs you play is `q`, then the minimum time you will need to wait because of the tone is always `q - p`. So, let us pick the minimum and maximum tone of the songs you will play, `O(n^2)` options for this pair. The rest is just to play the best song durations between these tones such that the total duration is less than or equal to `T - q + p`.

int maxSongs(vector <int> duration, vector <int> tone, int T)
{ 
    //sort the tones:
    int n = tone.size();
    for (int i=0; i<n; i++) {
        for (int j=i+1; j<n; j++) {
            if (tone[j] < tone[i]) {
                swap(tone[j], tone[i]);
                swap(duration[j], duration[i]);
            }
        }
    }
    int res = 0;
    // pick the minimum and maximum tone:
    for (int a=0; a < n; a++) {
        for (int b=a; b < n; b++) {
            int t = T - tone[b] + tone[a];
            int c = 0;
            // save the durations in a vector
            int tim = 0;
            vector<int> d;
            for (int i=a; i<=b; i++) {
                d.push_back(duration[i]);
            }
            // sort the durations, so that we pick the smallest c durations:
            sort(d.begin(), d.end());
            for (int x: d) {
                if (x <= t) {
                    t -= x;
                    c ++;
                }
            }
            // remember the best
            res =std::max(res, c);
        }
    }
    return res;
}

Saturday, July 27, 2013

SRM 586 : Meh

Another bad day.

Div1 500 : Something with dynasties

Not sure what to do. I reduced it to finding out if there is a solution to a system of inequalities, where the variables are the (real) starting year of each nation's calendar. I had issues even implementing a stub code. I couldn't debug them because my valgrind was suddenly not providing line numbers. The other day, before releasing my test setup I actually tweaked my c++ build file a bit, I may have done something that gives valgrind line number amnesia. I actually spent most of the last 10 minutes (before the 10 minute mark) trying to fix this. Not like I had an actual idea of how to solve this problem.

Div1 250: The one with a function.

You are given an array `Y = { Y_0, Y_1, ... Y_(n-1) }`, the array talks about a real function `f()` with domain `[Y_0, Y(n-1)]`. For each `(i < n - 1)`, the function contains line segment between points `(i,Y_i)` and `(i+1, Y_(i+1))`.

Find an equation `y = "something"` that intersects the largest number of times with this function. And return that number of times. Of course, if a horizontal segment exists in `f()` there exists a `y` that will have infinitely many intersections, in that case return -1.

This was a good problem and I felt confident to solve it under 10 minutes. However, I had a bug (didn't notice an issue with code) which delayed me a bit past the end of the coding phase. According to KawigiEdit, I would have scored ~209 points in this problem if I opened it first. Too slow. Then it turns out that my idea was wrong anyway. So I don't think things changed much by my strategy. If I was having a good day, I *may* have found the challenge case , and maybe attempted to challenge. Who knows? I am still preferring to use my new strategy, whilst 250 is tricky, I learned a bit more by trying to solve div1 500 than by solving yet another tricky 250.

Anyway, the solution was to notice that most of the times we only need to try values from `Y[]` as the position of the horizontal line that crosses f(). This is because any intermediary point in a segment will intersect an equal or lesser number of times than the segment's extremes. So just count, for each `Y[i]`, the number of times it intersects with segments in the function

So does `y` intersect with a segment that goes from `Y[i-1]` to `Y[i]`?, if `y` lies in interval `[Y[i-1], Y[i] ]`, the answer is yes. However, the mistake I made was that I was counting some intersections twice. The same point `(x,y)` may be shared by two segments, and you only need to count once. What I did to fix this small issue was to make sure that `y` is not equal to `Y[i-1]` before counting that intersection.

However, there is a catch and it is that there are exceptions to the rule. Imagine `Y = {0, 5, 0, 5}`, in this case, neither 0 or 5 are optimal locations. 2.5 is better (3 intersections). What is up with that?

It turns out that, besides of the segment's extremes, you need to take a point (any point) between any two extremes of a segment. You only need to check once per pair of segment extremes. In fact, you can just check for every segment extreme +- 0.5 and it will suffice. +- 0.5 can be implemented easily by scaling all values by 2 so you just have to try +- 1. I hope to have a formal proof by the time the editorial is released.

int maximumSolutions(vector <int> Y)
{
// If there is a horizontal segment, return -1:
for (int i=1; i<Y.size(); i++) {
if (Y[i] == Y[i-1]) {
return -1;
}
}
// Scale up all values by 2:
for (int i=0; i<Y.size(); i++) {
Y[i] *= 2;
}
int mx = 0;
// for each y such that abs(y - Y[i]) <= 1:
for (int i=0; i<Y.size(); i++) {
for (int y = Y[i] - 1; y <= Y[i] + 1; y++) {
//count the number of intersections:
int c = (Y[0] == y);
for (int j=1; j<Y.size(); j++) {
if (Y[j] > Y[j-1]) {
c += ( (Y[j-1] < y) && (y <= Y[j]) );
} else {
c += ( Y[j] <= y && y < Y[j-1] );
}
}
mx = std::max(mx, c);
}
}
return mx;
}

Comments, etc

Ok match, div1 500 had a too complex statement. Div1 250 was good. I am very disappointed by the low amount of python submissions. Hopefully it is because the python coders are still not aware of the news that it can be used.

Saturday, June 01, 2013

Out of google codejam 2013

That was a very bad day. At least I redeemed myself during the last few minutes and managed to submit something. Not good enough. If I was faster I could have at least gotten a t-shirt.

I was feeling a bit slow-minded today. So I was nervous about this match, the first hour was all about me trying not to get distracted and focus. I wasted minutes formalizing the cost formula based on a single number of stations. I am not sure why.

One hour later, I gave up and decided to read the other problems. Problem B seemed interesting.

Problem statement

So I got clever, and thought that the second result can be calculated if you find the minimum index of a player that will ALWAYS lose. The second result is equal to that index minus 1. Always win and always lose are very similar problems. So similar, that Always-lose can be seen as the complete reverse of always-win and we can find a relation-ship between an always-lose function and an always-win one...

...That's what I thought at least. But I was having issues, because when I did the example cases in paper, the trick was not consistent.

Things didn't make sense and time was flying. Decided to open the other problems. At first I thought C-small could be done with meet-in-the-middle (and maybe it can), but this problem is probably a bit too theorical for me. D-small seemed like a heavy implementation problem, I didn't even read it.

Less than 40 minutes left, I was about to just call it a day and get out of the codejam site. But then I decide not to give up. B is the one problem that seemed the most solvable, so I decided to finally face it!

It turns out that in my first analysis, I thought that P was the worst rank of the competitor that gets a prize. Whereas it actually is the number of competitors... I am fairly sure I would have solved this problem 1 hour earlier if it wasn't for this stupid mistake.

After correctly reading the statement, there is still the little issue of actually solving the problem (index that is guaranteed to win)... I assumed that it involved some recursion and binary representation, but I didn't know exactly what to do.

I decided to write a bruteforce solution for N=3, (8 players). I wanted to make sure that the larger P, the larger the result. For some reason I had doubts... I made a solution that showed, for each P, all the indexes that win and all that lose. Good news, so my first assumption is true.

In a flash of very lucky intuition, I noticed a pattern. These are the results of my bruteforce:

P = 0 : LLLLLLLL
P = 1 : WLLLLLLL
P = 2 : WLLLLLLL
P = 3 : WLLLLLLL
P = 4 : WLLLLLLL
P = 5 : WWWLLLLL
P = 6 : WWWLLLLL
P = 7 : WWWWWWWL
P = 8 : WWWWWWWW

If the i-th string is W, the i-th player is guaranteed to win.

So note how the first 4 indexes (after 0) have only one W, the next 2 indexes have 3, and the next 1 index has 7...

Time was running short, and I had this strong suspicion that I could use this logic to fill everything. For example, for N=4 (16 players), indexes 1-8 have one W, 9-12 three Ws, 13-14 seven Ws and index 15 has 15 Ws. I tested my fate, and this silly , out of nowhere logic works for B-small, the better news were that it is easy to optimize it for N=50, and I could solve B-large too...

There were 7 minutes left for the match. I tried to open A again, maybe if I could somehow solve A-small I could at least ensure a place in the top 1000. But it wasn't possible. Even if I thought of a solution, coding it would have taken far more than 7 minutes. I spent the last 5 minutes looking at how my rank became worse and worse and further from the top 1000. When the match ended, I was in position 1120-th. The large solutions were evaluated and I was 1057-th. So that's it. Maybe if google penalizes 57 solutions, I can get a t-shirt. Although to be honest, all codejam t-shirts throughout the years look the same. Maybe they changed the design this year.

-------------------

Rant: Too much emphasis in long problems. Most people would have nothing to do. I blame the 30 minutes extension in the coding time. The good thing about the codejam format was that it allowed to have approachable problems as small inputs. But lately the small inputs are tough anyway. Are B and C-small really much easier than the large ones?

Wednesday, February 13, 2013

SRM 570: Blue

This is it, I am going blue today.

As I write this (Before challenge phase) I know that if it was not for a last second mistake, I would have actually had a good score in division 1 250. But this last mistake made me unable to submit.

Div1 250: The one with robot

A robot in an infinite grid has a program. For each integer a[i], it moves forward a[i] times, then rotates left a[i] times. This program is repeated T times, return abs(x) + abs(y) where (x,y) is the final position.

Let us represent directions by 4 integers: 0, 1, 2, 3. Rotating left x times really just does direction = (direction + x) % 4. The direction is an index for off set array, so moving by direction i means moving dx[i] units in x axis and dy[i] units in y axis. dx[] and dy[] should be in clockwise order.

The trick is to know that after running the program one time, the direction will change by either 0, 1, 2 or 3. x and y should also change.

If the direction changes by 0, repeating the program again will not change the direction, then final x = 4*x , final y = 4*y (where x,y are the position after running once).

If the direction changes by 1 or 3, then repeating the program FOUR times will take us to a total program that modifies the direction by 0. So we can just repeat four times to find out how many does this combined program change, multiply x and y by T/4, then repeat original program T%4 times.

If the direction changes by 2, then you just need to repeat 2 times.

// simulates program a one time, updating initial x,y and d. 
void doit(vector<int> a, long &x , long &y, int&d)
{
int dx[4] = {0, 1, 0, -1};
int dy[4] = {1, 0, -1, 0};
for (int i=0; i < a.size(); i++) {
x += dx[d] * a[i];
y += dy[d] * a[i];
d = ( d + a[i]) % 4;
}
}
long getdist(int T, vector <int> a)
{
long x = 0, y = 0;
int d = 0;
// run once to know how will d change:
doit(a, x,y,d);
// Select the number of times to repeat the program so that d=0
int r = 1;
if (d == 0) {
r = 1;
} else if ( (d == 1) || (d==3 ) ) {
r = 4;
} else {
r = 2;
}
// Simulate how it is to repeat the program r times (Already done once)
for (int i=0; i<r - 1; i++) {
doit(a, x,y,d);
}
x = x * (T/r);
y = y * (T/r);
// Repeat T%r more times:
for (int i=0; i<T%r; i++) {
doit(a, x,y,d);
}
return abs(x) + abs(y);
}

As usual, I only had 10 minutes to solve this problem. So I rushed a bit, and actually managed to do it all -- with the exception that in my rush I thought that when the direction changes by 3 you should repeat thrice instead of four. This was dumb because when I initially thought of the solution I knew it was 4 repetitions (The results modulo 4 are known after you do these problems for a while). But for some reason, when there was about one minute left, I did sums modulo 4 manually. I did 0+3 = 3 mod 4, 3+3 = 1 mod 4, then 1+3 = 0 mod 4. The mistake was that I accidentally had 3+3=1 mod 4, when it should be 2.

So I failed example cases. I had 40 seconds to debug. I didn't have time to find out what the issue was. Around 10 seconds after the end of the coding phase I figured what the mistake was.

But I think the real blunder was not to engineer the code better. I kept pasting stuff and redoing the bit of code for each d, I think this copy paste delayed me too much. This problem was workable for 10 minutes.

Later in the challenge phase, I found a solution that returned 0 in a case, so I challenged it (I knew that there was no difference between 0 and -25, so basically no risk). It turns out that was correct. That was clever actually, if d=2 after one run and T is even, the result is 0. There are other similar tricks. After that moment I just kept challenging it to gain more minuses.

Last words

Anyway, I am going to be blue today, but I will maintain my strategy. I think that with more practice I will be able to start solving div1 250s in less than 10 minutes without all these mess ups. And now that I am blue I have nothing to lose anymore. This was a consequence I foresaw after taking the decision to use this strategy. Short term rating is not the objective.

Of course, I spent the first 65 minutes of the match looking at div1 medium and hard. I think div1 medium should be meet in the middle (That n<=36 constraint?). Div1 hard might be my favorite kind of problem, super optimized dp. Or it might be some sort of brute force.

Thursday, September 13, 2012

SRM 556 : #@!$%!

I am getting tired of this. Every latest SRM is the same situation. Approachable div1 easy and div1 mediums thus everyone solves them. But I take too much time to solve the easy a lame mistake stops me from solving div1 medium. Thus I end up with very low score whereas most people have two problems. Rating is killed. Over and over and over again. Can we go back to the times with very hard problems? I am starting to miss them. At least when the div1 easy is hard a low score can still allow you to keep your yellow rating.

Div1 easy

Nothing to see here. Just a BFS. I took very long because I used the wrong variable in one index.

Div2 medium

You are given a stack of digits. Initially, place the top digit on a table. Then place the top of the remaining digits either to the left or right of the digit on the table. Repeat placing each top digit to the right or left of the group of digits in the table. When you are finished, a number will be generated.

The number should be the smallest possible number you can make that is larger than or equal to lowerBound. If it is impossible to make a number larger than or equal to lowerBound return ""

So, at first I thought it was a typical [use dp to build numbers by digits] problem. Then I noticed that the top rule makes it a bit harder than usual. Then I noticed that you can still solve it like that.

It is important to make sure to make a number greater than or equal to the bound. Thus we need to somehow add numbers from left to right. But there is a catch, at some situations you might to place a digit to the right. More formally, let us do the opposite. Instead of placing the top card in the center of the table, think of the problem as first placing the bottom-most card either to the left side or the right side of the available space. Repeat until you run out of cards.

Thus we start from the largest index of the stack of digits, and we have a decision to make: We can place it left or we can place it right. There are some details to cover. We need to remember if the number we are building in the left side is already greater than lowerBound (If it is greater than, then we can place any digit on the remaining left side, else we can only post digits greater than or equal to the specific digit position in lowerBound. Thus we will call a variable greater.

The other catch is that, when placing digits to the right side, they might be smaller than the specific digit position. Note that this means that later moves should be done in such a way that the number is guaranteed to be greater before the right-most index is reached. This will be the variable mustGreater.

In the base case, we will reach a moment in which all available positions to the left and right are used - except a last one. In this case, we will have only one digit left. We must ensure that the digit follows the rules (If the digit is not greater yet, then the digit must be greater than or equal; Also make sure that if mustGreater, the number should be greater after placing this digit.

This is where my mistake was. I missed one special case (and I blame the examples for being so simple that this was not reached). What if mustGreater is true, but we place a greater digit to the right side? This means that the mustGreater condition has already been fulfilled. But after this, notice that if one of the following digits placed to the right side is again smaller, then mustGreater must be set back to true.

yadda yadda yadda, this should translate to a dp:

struct LeftRightDigitsGame2 
{
int n;
string digits, lowerBound;
string dp[50][51][2][2];

// How to fill the remaining interval [a, b[ if:
// The left side is already greater <==> (greater)
// The right side must be precceeded by a greater number <==> (mustGreater)
string rec(int a, int b, int greater, int mustGreater)
{
string & res = dp[a][b][greater][mustGreater];
// We are using memoization, at first all contents of dp are "".
// but that is invalid because the result is either z or at least one digit.
if (res == "") {
if (a+1 == b) { // base case
// the position of the next bottom-most digit we have not
// used yet.
// We have used (a) digits for the left side and n-b digits for
// the right side.
// index 0 is the top, so do it in reverse...
int p = n - (a + n - b) - 1;


// "z" is the lex greatest string, a sort of infinite.
// Lex smallest string is the same as smallest number in this case.

// If left side it is already greater, then we can place any digit
if (greater || (digits[p] >= lowerBound[a])) {
// as long as the digit follows the mustGreater condition...
int ngreater = greater || (digits[p] > lowerBound[a]);
if ( !mustGreater || ngreater) {
res = digits[p];
} else {
res = "z";
}
} else {
res = "z";
}
} else {
res = "z";
// p: same stuff as above, really...
int p = n - (a + n - b) - 1;

// put digits[p] to the left side.
// - a is incremented.
// Make sure we only place smaller digits if left side is already "greater"
// maybe upgrade greater if we found a good case for that.
//
if (greater || (digits[p] >= lowerBound[a])) {
int ngreater = greater || (digits[p] > lowerBound[a]);
string tm = rec(a+1, b, ngreater, mustGreater);
if (tm[0] != 'z') {
res = std::min(res, string(1, digits[p]) + tm );
}
}
// put digits[p] to the right side.
// - b is decremented
// - If digits[p] is smaller than bound, then something before must be greater.
// - But if digits[p] is larger, then it can cancel a previous requirement.
// - But if some other digit in the future is smaller, we will need the requirement again
//
int nmust = (mustGreater || (digits[p] < lowerBound[b-1]) ) && (digits[p] <= lowerBound[b-1]) ;
string tm = rec(a, b-1, greater, nmust);
if (tm[0] != 'z') {
res = std::min(res, tm + string(1, digits[p]) );
}

// recursion works like that, if the decision leads to a valid case
// then you have the smallest number to fill the remaining interval...
// so append the digit you placed (to the left or the right) to
// generate the real candidate for smallest number. Take the minimimum
// out of both numbers.
}
}
return res;
}

string minNumber(string digits, string lowerBound)
{
this->n = digits.size();
this->digits = digits;
this->lowerBound = lowerBound;
string tm = rec(0, n, false, false);
return (tm[0] =='z') ? string("") : tm;
}
};

Div1 1000

Seemed interesting. My first idea was terribly wrong. Like a Max flow giving an capacity between source and a1 and between a2 and sink. And bn capacity between source and b1 and between b2 and sink. This approach was obviously wrong (because a path between b1 and a2 would count as flow sometimes). But maybe there was a solution to it?

Writer mentioned that the intended solution involves making compound nodes? Like (x,y) where x is the current position starting from a1 and y is the current position starting from b1. Maybe the writer meant something else. But this does sound like a good idea. hmnn.

Challenge phase

I discovered a code with a bug. But somehow the test case I crafted did not catch the bug. I have to investigate further once statistics re-appear (must be something silly). So to the top of thing, I lost 25 points in challenge. If I failed div1 easy as well, then the negative score would have kicked me to the worst rating in years. It is rule #1: DO NOT challenge.

Any questions?

As usual, feel free to place comments, questions, rants, opinions and corrections in this post's comments section.

What an awful sequence of terrible matches for me. No, the problem sets were all fine in these recent SRMs. But this situation is killing my rating. I am really mad this time, because without the hindsight in div1 500 I would have been in top 20. Without the failed challenge in top 150. And if I actually did the challenge correctly (the code WAS wrong) in top 100.