Zero Wind – Jamie Wong Inside the mind of a Waterloo Software Engineering student

11May/101

Google Codejam 2010 Solutions – Qualification

I'm happy to have qualified for Codejam for the second year running - I managed to make it to Online Round 2 last year, so let's see if I can top that this year.

The opening round was not very difficult, and were it not for a very stupid mistake on my part on the first question, I easily could have gotten perfect in under 2.5 hours. Instead I got 76 in 3.5 hours. Bah.

Read the questions here: Qualification Round 2010 @ code.google.com

Problem 1: Snapper Chain

This is the one I screwed up, because I somehow overlooked the fact that each snap was simply equivalent to an binary increment of the snappers states. Instead, I did a foolish simulation, which did actually pass me the small test case. But anyway, the real solution is O(1).

For interest, here's what the snapper chain looks like in the first 30 iterations. The left side, labeled "P:" shows which of the snappers are powered, and the right side, labeled "S:" shows the current state of the snappers, with "#" being ON and "." being OFF in both cases. I did this so it would be easier to see the pattern. The first row is the initial set up, before any snaps.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
P: #.........	S: ..........
P: ##........	S: #.........
P: #.........	S: .#........
P: ###.......	S: ##........
P: #.........	S: ..#.......
P: ##........	S: #.#.......
P: #.........	S: .##.......
P: ####......	S: ###.......
P: #.........	S: ...#......
P: ##........	S: #..#......
P: #.........	S: .#.#......
P: ###.......	S: ##.#......
P: #.........	S: ..##......
P: ##........	S: #.##......
P: #.........	S: .###......
P: #####.....	S: ####......
P: #.........	S: ....#.....
P: ##........	S: #...#.....
P: #.........	S: .#..#.....
P: ###.......	S: ##..#.....
P: #.........	S: ..#.#.....
P: ##........	S: #.#.#.....
P: #.........	S: .##.#.....
P: ####......	S: ###.#.....
P: #.........	S: ...##.....
P: ##........	S: #..##.....
P: #.........	S: .#.##.....
P: ###.......	S: ##.##.....
P: #.........	S: ..###.....
P: ##........	S: #.###.....

What we're interested in is how to tell where a specific snapper is on. It turns out that this is quite simple - so long as all snappers before it are on the ON settings, it will be powered. This means that ultimately, the power column above is irrelevant. All we need to know is that the N-1 least significant bits are set. This is fairly trivial to check.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
using namespace std;
int main() {
	int T,N,K;
	cin >> T;
	for(int t = 0; t < T; t++) {
		cin >> N >> K;
		int mask = (1 << N) - 1;
		printf("Case #%d: ",t+1);
		if ( (mask & K) == mask) puts("ON");
		else puts("OFF");
	}
	return 0;
}

Time for the large case:

1
2
3
4
5
  [google]  time ./a.out < snapper.in > snapper.out
 
real    0m0.071s
user    0m0.026s
sys     0m0.040s

Problem 2: Fair Warning

This problem took me about 40 minutes to think of the solution for and about 5 minutes to code. Honour's Algebra (MATH 115) came in handy here. You can first calculate T (the optimal anniversary) by looking at the gcd (greatest common divisor) of the differences between the time elapsed since the events. This works because in order for T to divide the time elapsed since the events, it must also divide the time elapsed between the events. After that, you just need to find the first y that makes any of the numbers divisible by T. I just wrote out the congruences on my whiteboard (which I probably shouldn't have needed to do) then typed in my answer.

The only remaining problem here is dealing with the large numbers, which is hardly a problem in python. Here's my solution:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def gcd(x,y):
    while x:
        x, y = y % x, x
    return y
 
testcases = open("warning.in").readlines()
t = 1
for tc in testcases[1:]:
	tc = map(lambda x: int(x), tc.split(" ")[1:])
	if len(tc) == 0:
		break
	tc.sort()
	#print tc
 
	diff = []
	for i in range(len(tc)-1):
		diff += [tc[i+1] - tc[i]]
 
	T = diff[0]
	for d in diff[1:]:
		T = gcd(T,d)
 
	#print T
	print "Case #%d: %d" % (t, (-tc[0] % T))
 
	t += 1

Time for large case:

1
2
3
4
5
  [google]  time python warning.py > warning.out
 
real    0m0.250s
user    0m0.037s
sys     0m0.021s

Problem 3: Theme Park

This is an optimization problem. There are a lot of different optimizations you can apply, but I figured "to hell with it" and decided the solution getting it down to O(R) was probably sufficient. If you want to look at a better list of optimizations, go look here: Google Codejam 2010 Qualification Round Contest Analysis.

All I figured I needed to do was make it so I didn't have to find the groups every single time the ride was loaded. To do this, I simulated using queue until I arrived at a position where the group at the front of the line had been at the front before. While doing this, I record how many euros were made for a given start of the line, and who ends up at the front of the line while they're on the roller coaster. After this, the O(R) loop is very simple. Just add the number of euros for the run, then move on to the next front of the line.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <cmath>
#include <algorithm>
#include <iostream>
#include <fstream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>
#include <set>
#include <map>
#include <sstream>
#include <vector>
using namespace std;
 
#define FR(i,a,b) for(int i=(a);i<(b);++i)
#define FOR(i,n) FR(i,0,n)
#define SETMIN(a,b) a = min(a,b)
#define SETMAX(a,b) a = max(a,b)
#define PB push_back
#define FORALL(i,v) for(typeof((v).end())i=(v).begin();i!=(v).end();++i)
#define CLR(x,a) memset(x,a,sizeof(x))
#define BEND(v) v.begin(),v.end()
#define MP make_pair
#define A first
#define B second
 
typedef unsigned long long int ull;
typedef long double ld;
 
int main() {
    freopen("themepark.in","r",stdin);
    freopen("themepark.out","w",stdout);
 
    int T;
    cin >> T;
    FOR(t,T) {
        int R,k,N;
        cin >> R >> k >> N;
        int grps[1001], done[1001], pts[1001], next[1001];
        CLR(done,0);
        queue<pair<int,int> > q;
        FOR(i,N) {
            cin >> grps[i];
            q.push(MP(i,grps[i]));
        }
 
        while(!done[q.front().A]) {
            int cur = 0;
            int start = q.front().A;
            done[start] = 1;
 
            FOR(i,N) {
                if (cur + q.front().B > k) break;
 
                cur += q.front().B;
                q.push(q.front());
                q.pop();
            }
            next[start] = q.front().A;
            pts[start] = cur;
        }
 
        while (!q.empty()) q.pop();
 
        int curFront = 0;
        ull ans = 0;
        FOR(i,R) {
            ans += pts[curFront];
            curFront = next[curFront];
        }
 
        printf("Case #%d: %lld\n", t+1, ans);
    }
    return 0;
}

Time for large test case:

1
2
3
4
5
  [google]  time ./a.out
 
real    0m11.606s
user    0m11.542s
sys     0m0.022s

I might code up an O(N) solution later, which really isn't that difficult, but at this point in the contest, it simply didn't matter enough.

12Nov/094

SPOJ Problem 2: Prime Generator (PRIME1)

Problem: Prime Generator

Peter wants to generate some prime numbers for his cryptosystem. Help him! Your task is to generate all prime numbers between two given numbers!

Concept The idea behind every solution here (with some variation) is to generate all the prime numbers that could be factors of numbers up to the maximum endpoint 1 billion. That square root happens to be around 32000. Using this array, do a bounded Sieve of Eratosthenes only in the range requested. In languages like php and python, it turns out that it's more efficient to build an associative array and check if the index is set than it is to generate a huge boolean array.

Code

C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/// PRIME1 - C++ (g++)
// AC Time: 2.52s
// NOTE: I am aware that the use of vector and set actually
//  makes this code run _slower_
// I used vector and set simply as a way of practicing STL
#include <iostream>
#include <cmath>
#include <vector>
#include <set>
using namespace std;
 
int main() {
    vector<int> primes;
    primes.push_back(2);
 
    for (int i = 3; i < = 32000; i+=2) {
        bool isprime = true;
        int cap = sqrt(i) + 1;
 
        vector<int>::iterator p;
        for (p = primes.begin(); p != primes.end(); p++) {
            if (*p >= cap) break;
            if (i % *p == 0) {
                isprime = false;
                break;
            }
        }
        if (isprime) primes.push_back(i);
    }
 
    int T,N,M;
 
    cin >> T;
 
    for (int t = 0; t < T; t++) {
        if (t) cout << endl;
 
        cin >> M >> N;
        if (M < 2) M = 2;
 
        int cap = sqrt(N) + 1;
 
        set<int> notprime;
        notprime.clear();
 
        vector</int><int>::iterator p;
        for (p = primes.begin(); p != primes.end(); p++) {
 
            if (*p >= cap) break;
            int start;
 
            if (*p >= M) start = (*p)*2;
            else start = M + ((*p - M % *p) % *p);
 
            for (int j = start; j < = N; j += *p) {
                notprime.insert(j);
            }
        }
 
        for (int i = M; i <= N; i++) {
            if (notprime.count(i) == 0) {
                cout << i << endl;
            }
        }
 
    }
    return 0;
}

8Nov/090

SPOJ Problem 1

When I saw that Thor had done solutions to the first few problems of Project Euler in many many languages, I thought "Hey! That's a good idea!" but didn't want to copy exactly.

So instead I'm doing solutions to some problems on SPOJ. SPOJ is an online judge system full of algorithmic problems. This is one of the things that Hanson Wang did to get as good as he is. The solutions I'll be providing here are going to be very simple problems, so don't expect any magic. The beautiful thing about SPOJ is the sheer number of languages it will judge. I figured this was the perfect playground to make sure my code worked in all the languages I try.

Problem: Life, the Universe, and Everything

Your program is to use the brute-force approach in order to find the Answer to Life, the Universe, and Everything. More precisely... rewrite small numbers from input to output. Stop processing input after reading in the number 42. All numbers at input are integers of one or two digits.

Solutions - In order of frequency that I use the language

C++

1
2
3
4
5
6
7
8
9
10
11
12
13
//TEST AC - CPP (g++)
#include <iostream>
using namespace std;
 
int main() {
	int n;
	while(1) {
		cin >> n;
		if (n == 42) break;
		cout << n << endl;
	}
	return 0;
}

C99

1
2
3
4
5
6
7
8
9
10
11
//TEST AC - (gcc C99)
#include <stdio.h>
int main() {
	int n;
	while(1) {
		scanf("%d",&n);
		if (n == 42) break;
		printf("%d\n",n);
	}
	return 0;
}

php

1
2
3
4
5
6
7
8
<?
//TEST AC - PHP
while(1) {
	fscanf(STDIN,"%d",$n);
	if ($n == 42) break;
	print "$n\n";
}
?>

Python

1
2
3
4
5
6
#TEST AC - Python
while 1:
	num = input()
	if (num == 42):
		break
	print num

Bash

1
2
3
4
5
6
7
8
9
10
#!/bin/bash
# TEST AC - BASH
 
while true; do
	read n
	if [ $n -eq 42 ]; then
		break
	fi
	echo "$n"
done

Ruby

1
2
3
4
5
6
7
8
#TEST AC - Ruby
while 1
	n = gets.to_i
	if n == 42
		break
	end
	puts n
end

Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//TEST AC - Java
import java.io.*;
import java.util.*;
 
public class Main {
	public static void main(String[] args) {
		Scanner in = new Scanner(System.in);
 
		while(true) {
			int n = in.nextInt();
			if (n == 42) break;
			System.out.println(n);
		}
	}
}

Perl

1
2
3
4
5
6
7
8
9
#TEST AC - Perl
while (1)
{
	$n = <STDIN>;
	if ($n == 42) {
		last;
	}
	print $n
}

C#

1
2
3
4
5
6
7
8
9
10
11
12
//TEST AC - C# (gmcs + Mono)
using System;
class WelcomeCSS {
	static void Main() {
		while(true) {
			int n;
			n = int.Parse(Console.ReadLine());
			if (n == 42) break;
			Console.WriteLine(n);
		}
	}
}

GNU Pascal

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{TEST AC - GPC Pascal}
 
program TEST;
 
var n:integer;
 
begin
	while true do
		begin
			readln(n);
			if n = 42 then begin
				break;
			end;
			writeln(n);
		end;
end.

You can see Thor's Project Euler solutions here: Derek Thurn's Website - Project Euler