Sep 4 2009

เฉลยกูเกิ้ลโค้ดแยม : รอบคัดเลือก 2009

Category: Generalm3rLinEz @ 09:19

ยังคงคอนเซ็ปต์เดิม ว่าต้อง “แยม” ไม่ใช่ “แจม” (ผมอยากกวนตรีนราชบัณฑิตอ่ะ ไม่มีไรหรอก - -‘’)

ปีก่อนลุยด้วย C# ปีนี้ลุยด้วย Python แทนครับ ผมชอบความเรียบง่ายของภาษานะ แต่ตอนนี้ติดอยู่ที่ไม่รู้จะ Debug ยังไงให้มันง่ายๆ อยากให้เหมือน C# แบบที่พอ Break แล้วจะพิมพ์โค้ดอะไรเข้าไปทดสอบ (ในหน้าต่าง Immediate) ก็ได้ ใครรู้บอกที

Alien Language

ข้อแรกค่อนข้างง่ายครับ ส่วนที่เสียเวลาน่าจะเป็นตอนพยายาม break ตัว input ที่เค้าให้มาเป็นส่วนๆ ผมลองผิดลองถูกกับ regex อยู่นานเหมือนกัน หลังจาก break ได้แล้วทุกอย่างก็ตรงไปตรงมา คือตัดตัวที่เป็นไปไม่ได้ออกจาก dict ไปเรื่อยๆ

Edit: แอบไปดูโค้ดรุจมา ใช้วิธีเปลี่ยน ( กับ ) เป็น [ กับ ] แล้ว treat as regex เลย … อึ้ง

import re

if __name__ == '__main__':
#in_filename = "A-small.in"
#in_filename = "A-dummy.in"
in_filename = "A-large.in"
#out_filename = "A-small.out"
out_filename = "A-large.out"

in_file = open(in_filename, 'r')
out_file = open(out_filename, 'w')

L,D,num_cases = [int(x) for x in in_file.readline().split(' ')]

# Read the dictionary
words = []
for i in range(0,D):
words.append(in_file.readline())


for c in range(0,num_cases):
# Clone a list
ws = words[:]
s = in_file.readline()
clues = re.findall('(\([a-z]+\)|[a-z])',s)
#print clues
for k in range(0,len(clues)):
ws = filter(lambda x: clues[k].find(x[k]) != -1, ws)
out_file.write("Case #%d: %d\n" % (c+1, len(ws)))

out_file.close()


Watersheds

ผมคิดว่าข้อนี้ยากที่สุดสำหรับปีนี้แล้ว โจทย์มันคล้ายๆกับการเทสีใน Paint คือจะใช้ floodfill ทำก็ได้ (มีตัวอย่างโค้ด Floodfill ภาษา Java ที่บลอกภาษาอังกฤษ)  หรือใช้วิธีอื่นก็ได้ อย่างที่ทำครั้งนี้เป็นการแสกนดูก่อนว่าจุดไหนบนแผนที่เป็นกลุ่มเดียวกันบ้าง (คล้ายๆเรื่อง Disjoint Set) แล้วค่อยทำการ Assign ตัวอักษรให้ใหม่ โปรแกรมนี้เขียนเละๆ และดีบักนานมากกก อาจจะเพราะไม่ได้เขียนนานแล้วแล้วก็ยังไม่ค่อยคุ้นกับวิธี Debug ใน Python ด้วย ใช้ “Printf Debugging” ตลอด ฮะๆ

import re
import sys

if __name__ == '__main__':
#in_filename = "B-small.in"
#in_filename = "B-dummy.in"
in_filename = "B-large.in"
#out_filename = "B-small.out"
out_filename = "B-large.out"

in_file = open(in_filename, 'r')
out_file = open(out_filename, 'w')

num_cases = int(in_file.readline())
for c in range(0,num_cases):
H,W = [int(x) for x in in_file.readline().split(' ')]
map = []
for h in range(0,H):
map = map + [int(x) for x in in_file.readline().split(' ')]
label = range(0,H*W)
assoc = [-1]*H*W

for h in range(0,H):
for w in range(0,W):
cursor = h*W + w
min = sys.maxint
way = -1

pos1 = (h-1)*W + w
if h != 0 and map[pos1] < min:
min = map[pos1]
way = 1

pos2 = h*W + w - 1
if w != 0 and map[pos2] < min:
min = map[pos2]
way = 2

pos3 = h*W + w + 1
if (w + 1) < W and map[pos3] < min:
min = map[pos3]
way = 3

pos4 = (h + 1)*W + w
if (h + 1) < H and map[pos4] < min:
min = map[pos4]
way = 4

# If is Sink
if min < map[cursor]:
if way == 1:
assoc[label[cursor]] = label[pos1]
elif way == 2:
assoc[label[cursor]] = label[pos2]
elif way == 3:
assoc[label[cursor]] = label[pos3]
elif way == 4:
assoc[label[cursor]] = label[pos4]

# Do paths compression
#print "Before", assoc
for ind in range(0,len(assoc)):
next = assoc[ind]
last = next
while next != -1:
last = next
next = assoc[next]
assoc[ind] = last
#print "Compress", assoc

# Final relabel
out_file.write("Case #%d:\n" % (c+1))
char_map = {}
running = ord('a')
for h in range(0,H):
for w in range(0,W):
cursor = h*W + w
if assoc[label[cursor]] != -1:
label[cursor] = assoc[label[cursor]]
if label[cursor] not in char_map:
char_map[label[cursor]] = running
running = running + 1

#print label[h*W:h*W + W]
out_file.write(' '.join([chr(char_map[x]) for x in label[h*W:h*W + W]]))
out_file.write('\n')


out_file.close()


Welcome to Code Jam

ดูเหมือนอันนี้จะง่ายกว่าข้อสอง ใช้ Dynamic Programming แก้ เป็นตารางขนาด len(‘welcome to code jam’) x len({text}) แต่ละแถวคือจำนวนคำตอบที่จบด้วยตัวอักษรที่คู่กับแถวนั้น  ปกติพวกโค้ด DP จะอ่านไม่ค่อยรู้เรื่องอยู่แล้วเพราะมันเป็นการเอาความสัมพันธ์เวียนเกิด (Recursion) ใน math มาเปลี่ยนเป็นโค้ด o__o’’

เข้าใจว่าข้อนี้ถ้าแก้ด้วย Brute force ก็น่าจะทันเวลา (ล่ะมั้ง) วิเคราะห์ไม่เป็นแล้ว :-o

Edit: คะแนนออกแล้วปรากฎว่า ลืมไปบรรทัดนึง ตรง answer = reduce( …. ต้องเปลี่ยนเป็น answer = reduce(..) % 10000 สะเพร่าเสมอต้นเสมอปลาย - -‘’

w, e, l, c, o, m, e,  , c, o, d, e,  , t, o,  , c, o, d, e,  , j, a, m,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0
0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, 3, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 6, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0


import re
import sys

if __name__ == '__main__':
#in_filename = "C-small.in"
#in_filename = "C-dummy.in"
in_filename = "C-large.in"
#out_filename = "C-small.out"
out_filename = "C-large.out"

in_file = open(in_filename, 'r')
out_file = open(out_filename, 'w')

num_cases = int(in_file.readline())
for case in range(0,num_cases):
text = in_file.readline()
sen = "welcome to code jam"
W = len(text)
H = len(sen)
tab = [0]*W*H
for r in range(0,H):
sum = 0
for c in range(0,W):
cursor = r*W + c
if r == 0 and text[c] == sen[0]:
tab[cursor] = 1
else:
sum = (sum + tab[(r-1)*W + c]) % 10000
if text[c] == sen[r]:
tab[cursor] = sum
#print tab[r*W:r*W+W]

# Sum last row
answer = reduce(lambda x,y: x + y, tab[(H-1)*W:H*W])
print "Case #%d ..." % (case + 1)
out_file.write("Case #%d: %04d\n" % ((case+1), answer))
#print '-'*80
out_file.close()


เมื่อคืนนอนเร็วมากด้วยความอ่อนเพลีย เลยได้ตื่นมาเล่นตั้งแต่ 6 โมงเช้าครับ เขียนไปเรื่อยๆจน 9 โมง ติดข้อสองนานมากกกก

ปีนี้รู้สึกไม่ค่อยมีคน Active มาเล่นด้วยเท่าไหร่ คนรุ่นเดียวกันสงสัยจะทำงานไม่มีเวลาว่างกลับมาก็เหนื่อยแล้วล่ะมั้ง วันนี้ระหว่างวันก็ไม่ได้ยุ่งกับ Code Jam ได้แต่เปิด Score board มามองตาปริบๆ แล้วก็แอบๆทดเลขลงกระดาษไปพลางๆ พอดีวันนี้งานเข้าซะด้วย ฮือๆ รู้สึกนายแท็ปก็จะคล้ายๆกัน

ครั้งนี้ได้ใช้ Python คล่องมือขึ้นเยอะ (แต่ยังไม่รู้จะเอาไปทำอะไรดี .. อ้ะ) มีอะไรแนะนำผมคอมเม็นต์มาได้เลยนะครับ ^ ^ ยังเป็นมือใหม่อยู่ เพื่อนๆที่ทำด้วยกันลองเอาโค้ดขึ้นบลอกมาแบ่งกันดูด้วยก็ดี อยากเห็นวิธีของคนรอบตัวมากกว่าอยากเห็นวิธีของฝรั่ง/คนจีนในเน็ต : )

Edit: มีคนทักเรื่อง DP มาเยอะ (สองคนถ้วน o__o) จริงๆแล้วส่วนใหญ่โจทย์ที่เป็น DP ส่วนใหญ่มันจะ

  • เป็นโจทย์พวก Optimization เช่นหาน้อยที่สุด แพงที่สุด จำนวนมากที่สุด ระยะทางสั้นสุด
  • เป็นโจทย์ที่คำตอบของปัญหาใหญ่ๆมักจะคำนวณได้จากปัญหาเล็กๆ เช่น factorial, fibonacci, …
  • เวลาไม่รู้จะทำยังไงแล้ว (ฮ่าๆ)

อย่างข้อ 3 นี่ สมมติว่าให้ text มาเป็นคำว่า “d d o o o g d d” และให้หาคำว่า “d o g”

  • แถวแรก (คู่กับตัว d) จะแทนจำนวน seq ที่เป็นไปได้ที่ลงท้ายด้วยตัว d นั่นก็คือตำแหน่งที่เป็นตัว d จะเป็น 1 ส่วนที่อื่นเป็น 0
    ได้ 1 1 0 0 0 0 1 1
  • แถวที่สอง (คู่กับตัว o) เป็นจำนวน seq ที่ลงท้ายด้วย o คือ “d o” ช่องที่จะเป็นไปได้ก็คือช่องที่เป็นตัว o เท่านั้น แต่จะมีจำนวนเท่าไหร่ก็ขึ้นกับว่าก่อนหน้าตัวมันเองมี seq “d” ให้ใช้ทั้งหมดกี่อัน ทำได้โดยการเอาแถวข้างบนบวกกันจนถึงตัวมันเอง
    ได้ 0 0 2 2 2 0 0 0
  • แถวที่สาม (คู่กับตัว g) ทำแบบเดียวกับแถวสอง คือเติมเฉพาะช่องที่เป็น g และนับจำนวน seq ก่อนหน้าที่เป็น “d o”
    ได้ 0 0 0 0 0 6 0 0
  • เอาแถวสุดท้าย sum ทั้งแถว ได้จำนวนรูปแบบทั้งหมดที่เป็นไปได้

Tags: , , ,

Comments

1.
TAP TAP says:

ไม่เคยทำ dynamic programming ได้เลย ตั้งแต่สมัยเรียนละ ไม่เข้าใจอะ

2.
D.C.Mote D.C.Mote says:


. . . ผมทำได้ข้อเดียวอ่ะ(แน่นอน ข้อแรก C++)

กว่าจะได้อ่านโจทย์ก็ 6 โมงเย็นแล้วอ่า - -'
เรียนทั้งวัน  เช้าData Mining+Formal บ่ายSE (ห้องที่เรียนก็อับสัญญาณเน็ท)

3.
หมากรุกจีน หมากรุกจีน says:

ไม่เคยเข้าถึงศาสตร์ของ Dynamic Programming ได้เลยเช่นกัน T-T
ว่าแต่เขาแอบดูโค้ดกันตรงไหนอ้ะ

4.
m3rlinez m3rlinez says:

@TAP สรุป groovy เอามาใช้ไม่ทันเรอะ

@D.C.Mote วิชาเรียนน้องโคดคุ้นๆเลยนะครับ มีอยู่ที่เดียวมังใช้ชื่อวิชา Formal เนี่ย 55+

@หมากรุกจีน มันต้องติ๊กตรง "Solution Download" ก่อนอ่ะ แล้วจะขึ้นมาให้เลือก

เขียนเรื่อง DP เพิ่มอีกนิดละ

5.
TAP TAP says:

อ่านอยู่วันเดียวก็เลิกละ รู้สึกว่ามันจะหาโอกาสใช้ในอนาคตยาก ตอนนี้กำลังฝักใฝ่กับ Scala อยู่ แต่ยังไม่พร้อมเลย

6.
D.C.Mote D.C.Mote says:

ถามหน่อยครับ

คือ  ในGCJเนี่ย  เค้าให้เราโหลดlibraryจากที่อื่นไปใช้ไหมครับ?(อย่างข้อ 2 ผมคิดจะไปหาDisjoint Setมาinclude  แล้วทำล่ะครับ)

แล้วก็  ปี4 รุ่นนี้  เค้าสนใจIEEEXtreme Programming Contest 3.0กันมากกว่าGCJมั้งครับ(แข่งกลุ่มละ 3 คน / 24 ชม.)

7.
m3rlinez m3rlinez says:

@D.C.Mote ยังไม่ได้ไปอ่านกฎแต่ผมว่าเค้าไม่น่าจะแคร์ว่ะ พูดง่ายๆคือป้องกันอะไรไม่ได้ (ช่วยกันทำคนละข้อ, แบ่ง test case ให้กัน, เอาโค้ดไปรันบน grid, ..) ยกเว้นไอ่รอบที่ต้องไปแข่งที่ site ที่จัดไว้อาจจะมีข้อจำกัดเยอะขึ้นหน่อย (แค้ชีวิตนี้ผมคงไม่ได้ไปรอบนั้นว่ะ 555+) อิจฉาน้องปีสี่รุ่นนี้นะ ได้ไปเที่ยวไกลๆ + แข่งเขียนโปรแรมด้วย Tong

8.
Sikachu! Sikachu! says:

โอ ..

ขอบคุณมากๆ เลยครับที่ทำให้ผมเห็นกระจ่างเลยว่าข้อ 3 นั้นคิดอย่างไร
ผมคิดไม่ออกเลยล่ะครับ ๕๕๕

แต่ผ่านเข้ารอบ เพราะมันผ่าน small แต่ไม่ผ่าน large set -*-

Add comment


(Will show your Gravatar icon)

biuquote
  • Comment
  • Preview
Loading