Google Code Jam 2011に登録完了
Google Code Jam 2011
昨年に続いてGoogle Code Jamに参加します。昨年はQualification Roundは突破できましたが、Round 1ではSub-Round AからCまで全て参加しましたが尽く敗退し、苦い経験となっています。
昨年は競技プログラミングに出会い、PKU Online Judgeで鍛え、ACM-ICPCにも参加しました。最近はというと、チームでのシステム開発プロジェクトを経験したり、iPhoneアプリの開発をしたりと実務的なプログラミングが多く、競技プログラミングはご無沙汰気味でしたが、今回のGCJを機に少しずつ再開できたらと思います。プログラミングを好きになった原点なので!
過去問
GCJの公式サイトから過去問に取り組むことができます。少しでも競技プログラミングのカンを取り戻すために昨年のQualification Roundの問題に取り組みました。
Snapper Chain
指パッチンでON/OFFが切り替わるデバイスSnapperを使って電球を点灯させる問題。昨年は律儀にSnapperの動作をシュミレーションし、見事にLargeで落ちてしまいました。実は電源から連結されたSnapperのON/OFFの動作は二進数のビットそのもので、これに気づくとすごく簡単に、かつ高速なプログラムを書くことができます。実にGCJらしい問題だと思います。
昨年も載せましたがソースコードを再掲。
#include <cstdio> using namespace std; int T, N, K; int main() { scanf("%d", &T); for (int t = 1; t <= T; ++t) { scanf("%d %d", &N, &K); int mask = (1<<N)-1; printf("Case #%d: %s\n", t, (mask&K)==mask?"ON":"OFF"); } }
Fair Warning
未だに問題の意味がわかりません。"slarboseconds"をググッても意味がわからずにパニクった記憶があります。どうやら最大公約数がポイントらしいということはわかるのですが、Largeでは10^50まで扱わなければならないため、私の場合はJavaでBigIntegerを使わなければなりませんでした。そういうこともあり手を付けていなかった問題。
Javaも良く分からないので今年は多倍長整数をどうしようと思っていたのですが、GCJでは使用するプログラミング言語に制約がないのでRubyを使ってみることにしました。Rubyも基礎程度しか知らないのですが、Javaよりも多倍長整数を扱いやすいので今年はこちらで行ってみます。
def calc_gcd(a, b) return b!=0?calc_gcd(b, a%b):a end class FairWarning def initialize @C = gets.chop.to_i puts @C for c in 1..@C a = gets.chop.split @N = a[0].to_i ts = [] for i in 1..@N ts << a[i].to_i end result = solve(ts) print "Case ##{c}: #{result}\n" end end def solve ts ts = ts.sort ds = [] for i in 0..ts.size-2 d = ts[i+1]-ts[i] unless d == 0 ds << d end end gcd = ds[0] for i in ds gcd = calc_gcd(gcd, i) end ret = ts[0] % gcd unless ret == 0 ret = gcd - ret end return ret end end FairWarning.new
Theme Park
ジェットコースター大好きグループがN組いて、各グループはg1, ..., gN人。ジェットコースターの定員はk人で、同じグループの人がバラバラに乗るのは嫌がるし、順番を抜かされるのも嫌がる。乗り終わったグループは列の後ろに並ぶ。一日R回ジェットコースターを運行するとして、一日の売上は何ユーロか?(1ユーロ/人)
これも単純にシュミレーションをするとLargeでTLEになってしまう。グループの数は高々10^3なので、全グループについてそのグループを先頭としたときに乗れる人数とグループ数を予め計算しておけば良い。
#include <cstdio> using namespace std; int T, R, k, N; int main() { scanf("%d", &T); for (int t = 1; t <= T; ++t) { scanf("%d %d %d", &R, &k, &N); int gs[N]; for (int i = 0; i < N; ++i) { scanf("%d", &gs[i]); } int nps[N]; int ngs[N]; for (int i = 0; i < N; ++i) { int np = 0, ng = 0; for (int j = 0; j < N; ++j) { if (np + gs[(i+j)%N] > k) break; np += gs[(i+j)%N]; ++ng; } nps[i] = np; ngs[i] = ng; } int pos = 0; long euros = 0; for (int r = 0; r < R; ++r) { euros += nps[pos]; pos = (pos+ngs[pos])%N; } printf("Case #%d: %ld\n", t, euros); } }昨年よりも圧倒的に練習が足りないですが、とりあえず予選通過はしたいと思います!