Google Code Jam Round 1B 2014

Google Code Jam Round 1B 2014

A. The Repeater

文字列の組が与えられたとき、a→aaやaa→aという操作を繰り返して、全ての文字列を等しくする最小手数は?

各文字ごとに何文字に揃えるのかを全探索。

↓このコードはLarge落ちた。提出ミスだった。

def minify(s):
    r = ""
    for i in range(len(s)):
        if i==0 or s[i-1]!=s[i]:
            r += s[i]
    return r

def toarray(s):
    a = []
    for i in range(len(s)):
        if i==0 or s[i-1]!=s[i]:
            a += [1]
        else:
            a[-1] += 1
    return a
    
def solve(S):
    if len(set(minify(s) for s in S)) > 1:
        return "Fegla Won"

    A = [toarray(s) for s in S]

    ans = 0
    for p in range(len(A[0])):
        a = 99999999
        for x in range(
            min(a[p] for a in A),
            max(a[p] for a in A)+1):
            a = min(a, sum(abs(a[p]-x) for a in A))
        ans += a
    
    return ans

for i in range(input()):
    N = input()
    S = [raw_input() for _ in range(N)]
    a = solve(S)
    print "Case #%s: %s" % (i+1, a)

B. New Lottery Game

正数A, B, Kに対して、a

xがXより小さいとは、Xのiビット目が1で、xのiビット目が0で、iより上位のビットではXとxが等しいというiが存在することである。A, B, Kそれぞれに対してそのようなビットの位置を決め、それを満たす(a,b)が何通りあるかを数えた。場合分けが煩雑になってしまったが、GCJではまずはナイーブなプログラムでSmallを通し、LargeのプログラムはSmallの入出力でテストするという手が使える。

Small

def solve(A, B, K):
    r = 0
    for a in range(A):
        for b in range(B):
            if (a&b)<K:
                r += 1
    return r

for t in range(input()):
    A, B, K = map(int, raw_input().split())
    print "Case #%s: %s" % (t+1, solve(A,B,K))

Large

def solve(A, B, K):
    r = 0
    for a in range(32):
     if A>>a&1:
        for b in range(32):
         if B>>b&1:
            for k in range(32):
             if K>>k&1:
                t = 1
                for i in range(32):
                    if i<k:
                        if i<a and i<b:
                            t *= 4
                        elif i<a or i<b:
                            t *= 2
                        else:
                            t *= 1
                    else:
                        x = K>>i&1
                        if i==k:
                            x = 0
                        if i<a and i<b:
                            t *= [3, 1][x]
                        elif i<a or i<b:
                            if i<a:
                                y = 0 if i==b else B>>i&1
                            else: # i<b
                                y = 0 if i==a else A>>i&1
                            if x==0 and y==0:
                                t *= 2
                            elif x==0 and y==1:
                                t *= 1
                            elif x==1 and y==0:
                                t *= 0
                            elif x==1 and y==1:
                                t *= 1
                        else:
                            y = (0 if i==a else A>>i&1) & (0 if i==b else B>>i&1)
                            if x==y:
                                t *= 1
                            else:
                                t *= 0
                r += t
    return r

for t in range(input()):
    A, B, K = map(int, raw_input().split())
    print "Case #%s: %s" % (t+1, solve(A,B,K))

C. The Bored Traveling Salesman

問題文の条件が分かりづらいけど、要は、

グラフの全域木を辿り行きがけ順で各ノードに与えられた文字列を連結する。辞書順最小の文字列を答えよ

どのノードを根としても当然全域木は得られるので、根は文字列が一番小さいノードを選べば良い。辞書順最小の文字列を答えるので、あとは移動可能なノードのうち文字列が最小のものを選んで行けば良い。移動可能なノードとは、現在地点からの帰り道の途中から移動できて、そのノードに移動したとしても残りの全てのノードを辿れるもの。残り全てのノードを辿れるかどうかは、これまでに通ったノードのうち帰り道以外のものを全て削除して、グラフが連結かどうかを調べれば良い。

import itertools

def solve(N, Z, F):
    p = Z.index(min(Z))
    ans = Z[p]
    S = [p]
    V = [False]*N
    V[p] = True

    for _ in range(N-1):
        next = -1
        for p in range(N):
            if V[p]:
                continue

            s = S[:]
            while len(s)>0 and not F[s[-1]][p]:
                s.pop()
            if len(s)==0:
                continue

            s += [p]
            v = V[:]
            c = [False]*N
            while len(s)>0:
                q = s.pop()
                v[q] = True
                for i in range(N):
                    if F[q][i] and not v[i]:
                        s += [i]
                    c[q] = True

            ok = True
            for i in range(N):
                if not v[i] and not V[i]:
                    ok = False

            if ok and (next<0 or Z[p]<Z[next]):
                next = p

        ans += Z[next]
        while not F[S[-1]][next]:
            S.pop()
        S += [next]
        V[next] = True

    return ans

for t in range(input()):
    N, M = map(int, raw_input().split())
    Z = [raw_input() for _ in range(N)]
    F = [[False]*N for _ in range(N)]
    for _ in range(M):
        a, b = map(lambda x: int(x)-1, raw_input().split())
        F[a][b] = F[b][a] = True
    print "Case #%s: %s" % (t+1, solve(N,Z,F))