programming.in.th · ข้อ 2005
ข้อที่สอนให้อ่านขอบเขตแปลก ๆ ของโจทย์ให้ออกว่ามันกำลังใบ้เฉลยอยู่ แล้วต่อด้วยการยุบเงื่อนไขทั้งข้อเหลือสองกฎ ซึ่งกฎหนึ่งพิสูจน์ยากพอที่จะต้องมีตัวตรวจสอบที่ไม่เชื่อทฤษฎีอะไรเลย
เมอโกกับสลาฟโกซ้อมปั่นจักรยานด้วยกัน คนที่ปั่นตามได้เปรียบเพราะคนนำช่วยบังลม ทั้งคู่จึงสลับกันนำทุกครั้งที่ถึงเมืองใหม่ เพื่อไม่ให้ใครได้เปรียบ ทั้งคู่จึงเลือกซ้อมบนเส้นทางที่ผ่านถนนเป็นจำนวนคู่
ในประเทศมีเมือง N เมือง ถนน M สาย ในจำนวนนั้นมี N − 1 สายที่ราดยาง
และเชื่อมทุกเมืองถึงกันพอดี นั่นคือถนนราดยางประกอบกันเป็นต้นไม้
ส่วนที่เหลือเป็นถนนที่ไม่ได้ราดยาง แต่ละเส้นมีค่าใช้จ่ายในการปิด
เส้นทางซ้อมคือวงปิดที่ออกจากเมืองหนึ่งแล้ววนกลับมาที่เดิม โดยไม่ซ้ำเมืองและไม่ซ้ำถนน คู่แข่งอยากขัดขวาง จึงจะปิดถนนที่ไม่ได้ราดยางบางเส้น (ปิดถนนราดยางไม่ได้) ให้ไม่เหลือเส้นทางซ้อมที่ยาวเป็นเลขคู่เลย งานของเราคือหาค่าใช้จ่ายรวมที่ต่ำที่สุด
อินพุต / ขอบเขต / เอาต์พุต
N และ M จากนั้น M บรรทัด
แต่ละบรรทัดคือ A B C ถ้า C = 0 คือถนนราดยาง ถ้า C > 0
คือถนนไม่ราดยางที่ปิดได้ด้วยค่าใช้จ่าย C 2 ≤ N ≤ 1000, N − 1 ≤ M ≤ 5000,
C ≤ 10,000 และทุกเมืองมีถนนต่ออยู่รวมไม่เกิน 10 สาย
เวลา 0.3 วินาที หน่วยความจำ 64 เมกะไบต์
| Input | Output |
|---|---|
| 5 8 2 1 0 3 2 0 4 3 0 5 4 0 1 3 2 3 5 2 2 4 5 2 5 1 | 5 |
สิ่งที่เราเลือกคือเซตของถนนไม่ราดยางที่จะปิด ตัวอย่างในโจทย์มีถนนแบบนั้น 4 สาย จึงมีเซตให้ลองแค่ 16 แบบ ไล่ดูทีละแบบได้สบาย
แต่ขอบเขตจริงมีถนนได้ 5,000 สาย ในนั้นเป็นถนนราดยาง N − 1 = 999 สาย ถนนไม่ราดยางจึงมีได้ถึง 4,001 สาย จำนวนเซตคือสองยกกำลัง 4,001
ด่านที่สองหนักกว่า ต่อให้มีคนยื่นเซตหนึ่งมาให้ การตรวจว่าใช้ได้ไหมต้องดูทุกวงปิดที่ยังเหลือ ซึ่งในภาษากราฟเรียกว่าวงจรอย่างง่าย (simple cycle คือวงที่ไม่ผ่านเมืองซ้ำ)
วงพวกนี้มีมากกว่าจำนวนถนนเยอะ ตัวอย่างเล็ก ๆ ในโจทย์มีถนนแค่ 8 สาย แต่มีวงจรอย่างง่ายอยู่ 12 วง แค่ตรวจคำตอบหนึ่งข้อก็แพงแล้ว
ทางออกจึงต้องยุบเงื่อนไขทั้งข้อให้เหลือสิ่งที่ตัดสินได้ระหว่างเดินบนต้นไม้ โดยไม่ต้องแตะวงปิดสักวง
ใบ้
ถนนราดยางเป็นต้นไม้ ดังนั้นถนนไม่ราดยางหนึ่งเส้นที่เชื่อม A กับ B
จะสร้างวงขึ้นมาพอดีหนึ่งวง คือเส้นทางบนต้นไม้จาก A ไป B บวกกับตัวมันเอง
ลองถามสองคำถามนี้ดู วงที่เกิดจากถนนเส้นเดียวยาวเท่าไร และถ้าเก็บถนนไม่ราดยางไว้สองเส้น ที่เส้นทางบนต้นไม้ของมันทับกัน จะเกิดวงอะไรขึ้นอีกบ้างที่เราไม่ได้ตั้งใจ
ห้าเมืองเดียวกับที่บทความใช้
กดปิดถนนที่ไม่ราดยางได้ตามใจ แล้วกดตรวจว่าเหลือเส้นทางซ้อมที่ยุติธรรมไหม
จ่ายไปแล้ว 0 · ถูกที่สุดคือ ?
เส้นทางซ้อมที่ยุติธรรมคือวงจรที่เดินแล้วกลับมาที่เดิม โดยผ่านถนนเป็นจำนวนคู่ ค่าที่ถูกที่สุดจะโผล่หลังกดตรวจครั้งแรก
ตัวตรวจของเกมนี้ไม่ได้ใช้กฎย่อของเฉลยเลย มันไล่หาวงจรทุกวงจริง ๆ ถ้าเดาแล้วผิด แปลว่ากฎในหัวยังไม่ตรงกับของจริง
ก่อนหาทางลัด เริ่มจากวิธีที่ตรงกับนิยามของโจทย์ที่สุด ซึ่งก็คือสิ่งที่ตัวตรวจของเกมข้างบนทำ
ตั้งแต่ตรงนี้ไป น้ำหนักของถนนไม่ราดยางคือค่าปิดของมัน มองจากฝั่งที่เก็บไว้ เก็บถนนที่หนักไว้ได้มากเท่าไร ก็จ่ายค่าปิดน้อยลงเท่านั้น
บนตัวอย่างในโจทย์ วิธีนี้ลอง 16 แบบแล้วได้ 5 ตรงกับเฉลย แต่ที่ขอบเขตเต็มมันต้องลองสองยกกำลัง 4,001 แบบ ซึ่งไม่มีทางจบ
ต้องหาทางตัดสินถนนทีละเส้นแทนการไล่เซต ไล่จากช้าไปเร็วมีสามวิธี วิธีที่ 1 คือตอนนี้ วิธีที่ 2 ใช้สองกฎจากตอนที่ 2 ถึง 4 และวิธีเต็มคือดีพีในตอนที่ 5 กับโค้ดในตอนที่ 6
วิธีที่ 1 · ตอนที่ 1
สองยกกำลัง K เซต คูณจำนวนวง
ตรงกับนิยามที่สุด ไม่ต้องเชื่อทฤษฎีอะไร (K คือจำนวนถนนไม่ราดยาง)
ได้แค่กราฟจิ๋ว ใช้เป็นตัวตรวจ
วิธีที่ 2 · ตอนที่ 2 ถึง 4
ยังเป็นสองยกกำลังจำนวนเส้น
กฎสองข้อทำให้ไม่ต้องหาวงอีกเลย ตรวจแค่ว่าเส้นทางบนต้นไม้ทับกันไหม แต่ยังต้องไล่เซต
ได้แค่ถนนไม่ราดยางไม่กี่เส้น
วิธีเต็ม · ตอนที่ 5 และ 6
ราว M × N + M × 210
ดีพี (dynamic programming การจำคำตอบของปัญหาย่อยไว้ใช้ซ้ำ) บนต้นไม้ แต่ละเมืองจำผลไว้ว่าถนนราดยางเส้นไหนที่ติดกับมันถูกจองไปแล้ว
ครบขอบเขต
ดูถนนไม่ราดยางทีละเส้นก่อน ถ้าเส้นทางบนต้นไม้ของมันยาว len สาย วงของมันยาว len + 1
กฎที่ 1
ถ้า len เป็นเลขคี่ วงยาวเป็นเลขคู่ ถนนเส้นนั้นต้องถูกปิดเสมอ ไม่ว่าจะเก็บเส้นอื่นไว้หรือไม่
ส่วนเส้นที่ len เป็นเลขคู่ วงของมันยาวคี่ ถ้าอยู่คนเดียวก็ไม่ผิดกฎ ชุดที่ 3 ของเกมคือกรณีนี้
| ถนน | ค่าปิด | เส้นทางบนต้นไม้ | ยาว | ตัดสิน |
|---|---|---|---|---|
1-3 | 2 | 1 - 2 - 3 | 2 | เก็บได้ ถ้าไม่ทับใคร |
3-5 | 2 | 3 - 4 - 5 | 2 | เก็บได้ ถ้าไม่ทับใคร |
2-4 | 5 | 2 - 3 - 4 | 2 | เก็บได้ ถ้าไม่ทับใคร |
2-5 | 1 | 2 - 3 - 4 - 5 | 3 | ต้องปิดเสมอ (วงยาวคู่) |
กฎที่ 1 ยังไม่พอ ถนนสองเส้นที่วงยาวคี่ทั้งคู่ อยู่คนเดียวได้ แต่เปิดพร้อมกันอาจเกิดวงคู่ (เกมชุดที่ 4 คือกรณีนี้)
ตัวอย่างในโจทย์ก็มีคู่แบบนั้น คือถนน 1-3 กับ 2-4
เส้นทางบนต้นไม้ของทั้งสองใช้ถนนราดยาง 2-3 ร่วมกัน
นับความยาวของวงใหม่
ส่วนที่ทับถูกลบสองครั้ง (ครั้งละวง) จึงหักออกเป็นเลขคู่เสมอ คี่บวกคี่ได้คู่ หักคู่ออกก็ยังคู่ วงใหม่จึงยาวคู่ทุกครั้ง ไม่ว่าจะทับกันกี่สาย
บนต้นไม้ สองเส้นทางพบกันเป็นช่วงเดียวต่อกันเสมอ (ต้นไม้ไม่มีทางแยกแล้วมาบรรจบกันใหม่) ส่วนที่ไม่ทับของสองเส้นทางจึงไม่มีเมืองร่วมกันเลยนอกจากปลายช่วงนั้น ตัดช่วงที่ทับออกแล้วได้วงเดียวที่ไม่ผ่านเมืองซ้ำ
กฎที่ 2
ถนนไม่ราดยางที่เก็บไว้ ห้ามมีสองเส้นไหนที่เส้นทางบนต้นไม้ใช้ถนนราดยางเส้นเดียวกัน
แตะกันแค่เมืองเดียวไม่นับว่าทับ เช่น 1-3 กับ 3-5
พบกันที่เมือง 3 อย่างเดียว วงสองวงนี้ต่อกันได้แค่แบบเดินผ่านเมือง 3 สองรอบ
ซึ่งไม่นับเป็นเส้นทางซ้อม
โจทย์บอกแค่ว่าปลายสองข้างของถนนเป็นคนละเมือง ไม่ได้ห้ามถนนสองเส้นเชื่อมคู่เมืองเดียวกัน สองกฎยังใช้ได้ครบ ถนนที่ขนานกับถนนราดยางมีเส้นทางยาว 1 จึงถูกปิดตามกฎที่ 1 ส่วนถนนไม่ราดยางสองเส้นที่ขนานกันมีเส้นทางเดียวกัน จึงทับกันตามกฎที่ 2 (สองเส้นนี้เองก็เป็นวงยาว 2)
ถึงตรงนี้ ปัญหาเหลือแค่นี้: เลือกถนนจากเส้นที่ผ่านกฎที่ 1 โดยเส้นทางไม่ทับกัน ให้น้ำหนักรวมมากที่สุด แล้วคำตอบคือค่าปิดทั้งหมดลบน้ำหนักที่เก็บได้
| ชุดที่เลือก | ถนนราดยางที่ถูกใช้ | น้ำหนักรวม |
|---|---|---|
1-3 เส้นเดียว | 1-2, 2-3 | 2 |
3-5 เส้นเดียว | 3-4, 4-5 | 2 |
2-4 เส้นเดียว | 2-3, 3-4 | 5 |
1-3 กับ 3-5 | 1-2, 2-3, 3-4, 4-5 | 4 |
ทำไมไม่เชื่อแค่การพิสูจน์
สองกฎข้างบนบอกว่าถนนแบบไหนต้องปิด แต่สิ่งที่ต้องการจริงคือทิศกลับ ถ้าเส้นทางไม่ทับกันเลย ต้องไม่มีวงคู่โผล่มาจากที่ไหนอีก ทิศนี้พิสูจน์ยากกว่า ผมไม่กล้าเชื่อจากการนั่งพิสูจน์อย่างเดียว
ผมจึงใช้โค้ดของตอนที่ 1 เป็นตัวตรวจ ตัวนี้ไม่รู้จักสองกฎเลย ถ้าคำตอบตรงกับโค้ดจริง ก็เป็นหลักฐานที่ไม่ได้อาศัยกฎที่กำลังตรวจ (ผลการสุ่มเทียบอยู่ในตอนที่ 6)
ส่วนเหตุผลแบบไม่ต้องสุ่ม มองวงเป็นเซตของถนน แล้วไปทีละขั้น
สองกฎกับทิศกลับรวมกันคือวิธีที่ 2 ของบันได ไม่ต้องหาวงอีกแล้ว แต่ถ้าเลือกชุดที่ไม่ทับกันโดยไล่ทุกเซต ก็ยังช้าเกินไป
ที่มาของแนวคิดนี้
สิ่งแรกที่ผมสะดุดตอนอ่านโจทย์คือขอบเขตที่ว่าทุกเมืองมีถนนไม่เกิน 10 สาย ขอบเขตเล็ก ๆ ที่แปะมาเฉพาะจุดแบบนี้ มักใบ้ว่าเฉลยต้องเอาไปใช้ตรง ๆ
เลข 10 เข้ากันพอดีกับการเก็บเซตของถนนรอบเมืองหนึ่งไว้ใน mask (หน้ากากบิต คือเลขฐานสองตัวเดียว บิตละหนึ่งสมาชิก) สองยกกำลัง 10 คือ 1,024 แบบ ไล่ครบได้ทุกเมือง
พอคิดแบบนั้น คำถามก็เปลี่ยนจาก "จะปิดถนนเส้นไหน" เป็น "ถนนรอบเมืองนี้เส้นไหนถูกจองไปแล้ว" ซึ่งตอบทีละเมืองได้
ใช้กับข้ออื่นได้ด้วย: เจอขอบเขตที่เล็กผิดที่ผิดทาง ให้ถามว่ามันทำให้อะไรที่ปกติแพงเกินไป กลายเป็นถูกลง
ตอนที่ 3 เหลือโจทย์ข้อเดียว คือเลือกถนนที่เส้นทางไม่ทับกันให้น้ำหนักมากสุด ไล่เซตไม่ได้ (ตอนที่ 1) ต้องตัดสินทีละเมือง
เริ่มจากปักรากต้นไม้ไว้ที่เมือง 1 แล้วแขวนเมืองอื่นลงมาเป็นชั้น ๆ คำที่ใช้ตลอดตอนนี้มีสี่คำ
เราจะคิดจากล่างขึ้นบน เมืองหนึ่งถูกคิดหลังลูกของมันทุกคนคิดเสร็จแล้วเสมอ ลำดับแบบนี้เรียกว่าลำดับหลังลูก (post-order)
เส้นทางของถนนแต่ละเส้นไต่ขึ้นจากปลายทั้งสองข้าง ไปพบกันที่เมืองที่สูงที่สุดบนเส้นทาง เมืองนั้นเรียกว่าปมร่วมที่ต่ำที่สุด (lowest common ancestor ย่อว่า LCA) ของปลายทั้งสอง
เส้นทางไม่ขึ้นเหนือปมร่วมเลย ถนนแต่ละเส้นจึงตัดสินที่ปมร่วมของมันได้ และเพราะคิดจากล่างขึ้นบน ถึงตอนนั้นทุกเมืองบนเส้นทางก็คิดเสร็จไปแล้ว
ตัวอย่างในโจทย์ใช้ตรงนี้ไม่ได้ ต้นไม้ของมันเป็นเส้นตรง ทุกเมืองมีลูกไม่เกินหนึ่งคน จึงไม่มีอะไรให้เลือกจอง ตอนนี้จึงใช้ตัวอย่างเล็กอีกชุดตลอดทั้งตอน ต้นไม้ 12 เมือง กับถนนไม่ราดยาง 7 เส้น ทุกเส้นยาวคู่ เพราะเส้นคี่ถูกกฎที่ 1 ตัดไปแล้ว
| ถนน | น้ำหนัก | เส้นทางบนต้นไม้ | ปมร่วม | ลูกของปมร่วมที่ใช้ |
|---|---|---|---|---|
4-5 | 3 | 4 - 2 - 5 | 2 | 4, 5 |
2-8 | 2 | 2 - 5 - 8 | 2 | 5 |
3-9 | 2 | 3 - 7 - 9 | 3 | 7 |
6-7 | 3 | 6 - 3 - 7 | 3 | 6, 7 |
4-6 | 9 | 4 - 2 - 1 - 3 - 6 | 1 | 2, 3 |
2-3 | 4 | 2 - 1 - 3 | 1 | 2, 3 |
10-12 | 3 | 10 - 11 - 12 | 10 | 11 |
นึกภาพ
นึกถึงผังองค์กร หัวหน้าแต่ละคนส่งตารางเล็ก ๆ ขึ้นไปให้นายของตัวเอง ทุกบรรทัดตอบคำถามเดียว "ถ้านายจองลูกน้องคนนี้ไปทำงานของนายแล้ว ทีมผมที่เหลือยังทำได้เท่าไร"
นายไม่ต้องรู้ว่าข้างใต้ใครทำอะไร อ่านแค่ตารางของหัวหน้าแต่ละคนก็ตัดสินใจได้
สถานะ dp[v][mask]
v คือเมือง (ในภาษากราฟเรียกว่าปม หรือ node) และ mask คือเซตของลูกของ v
ที่ถนนราดยางเส้น v ถึงลูกคนนั้นถูกเส้นทางจากข้างบนจองไปแล้ว ค่าของมันคือน้ำหนักรวมมากที่สุดของถนนที่เก็บได้ โดย
vmask ทั้งกิ่ง เส้นทางที่จองลูกคนนั้นจะคิดกิ่งนั้นให้เองv กับลูกใน mask
ลูกมีไม่เกิน 10 คน mask จึงมีไม่เกิน 1,024 แบบ คำตอบของส่วนนี้คือ dp[1][∅]
เติม dp[v][mask] สองทาง เอาทางที่มากกว่า
dp[ลูก][∅] ของลูกทุกคนที่ไม่อยู่ใน mask se ต้องไม่ชนกับ mask):
น้ำหนักของ e + ผลรวม dp ของเมืองบนเส้นทางของ e ที่อยู่ใต้ v + dp[v][mask ∪ se]
ผลรวมตามเส้นทาง: แต่ละเมืองบนเส้นทางใช้ค่า dp ที่จองลูกคนที่เส้นทางเพิ่งไต่ขึ้นมา ส่วนเมืองปลายทางใช้ [∅]
ถ้าปลายข้างหนึ่งคือ v เอง (เช่นถนน 2-8) ฝั่งนั้นไม่มีอะไรให้ไต่ ไต่แค่อีกฝั่งเดียว
mask ∪ se คือเซตของลูกที่ถูกจองหลังเก็บ e ไปแล้ว ในโค้ดเขียนว่า mask | se
คือเอาบิตของสองเซตมารวมกัน (OR) พจน์นี้ถามว่าลูกที่ยังว่างอยู่ ทำต่อได้อีกเท่าไร
ลองกับตัวอย่าง: ถนน 4-6 ที่เมือง 1
ทำไม dp[2][{4}] ได้ 2:
เมือง 2 ถูกจองลูก 4 ไปแล้ว
ถนน 4-5 ต้องผ่านลูกคนนั้นจึงใช้ไม่ได้ เหลือ 2-8 น้ำหนัก 2 ที่ยังเก็บได้
ลองเดินทั้งต้นไม้ทีละเมืองตามลำดับหลังลูก แต่ละขั้นเส้นสีคือเส้นทางของถนนที่ตัดสินที่เมืองนั้น บรรทัดที่ขึ้นต้นด้วย [ ] บอกว่ากำลังเติม mask ไหน
ต้นไม้ 12 เมือง ถนนไม่ราดยาง 7 เส้น คิดจากใบขึ้นไปหาราก
| เมือง | ลูก | ถนนที่ตัดสินที่นี่ | dp ตามลูกที่ถูกจอง |
|---|---|---|---|
| 4 | ใบ | · | ∅ 0 |
| 8 | ใบ | · | ∅ 0 |
| 5 | 8 | · | ∅ 0 · {8} 0 |
| 2 | 4, 5 | 4-5, 2-8 | ∅ 3 · {4} 2 · {5} 0 · {4,5} 0 |
| 6 | ใบ | · | ∅ 0 |
| 9 | ใบ | · | ∅ 0 |
| 7 | 9 | · | ∅ 0 · {9} 0 |
| 3 | 6, 7 | 3-9, 6-7 | ∅ 3 · {6} 2 · {7} 0 · {6,7} 0 |
| 12 | ใบ | · | ∅ 0 |
| 11 | 12 | · | ∅ 0 · {12} 0 |
| 10 | 11 | 10-12 | ∅ 3 · {11} 0 |
| 1 | 2, 3, 10 | 4-6, 2-3 | ∅ 16 · {2} 6 · {3} 6 · {2,3} 3 · {10} 13 · {2,10} 3 · {3,10} 3 · {2,3,10} 0 |
ในโค้ด d0[j] คือ dp[ลูกคนที่ j][∅], cs คือถนนที่ปมร่วมอยู่ที่ v
และ se คือบิตของลูกที่ถนนนั้นจอง ส่วน g คือแถว dp[v] ที่กำลังเติม
// g[mask] = น้ำหนักมากที่สุดในต้นไม้ย่อยของ v เมื่อลูกในเซต mask ถูกจองไปแล้ว
for (int mk = 0; mk < full; mk++) {
long long s = sum; // ค่าเริ่มต้นคือปล่อยลูกทุกคนทำงานของตัวเอง
for (int j = 0; j < k; j++) if (mk >> j & 1) s -= d0[j];
g[mk] = s;
}
// ลูปนอกไล่ mk จากค่ามากลงมาหาค่าน้อย (เหตุผลอยู่ในข้อ 3 ของเช็กความเข้าใจ)
for (int mk = full - 1; mk >= 0; mk--)
for (auto& c : cs) // cs = ถนนไม่ราดยางที่มีจุดร่วมสูงสุดอยู่ที่ v
if (!(c.se & mk)) g[mk] = max(g[mk], c.val + g[mk | c.se]);
ที่ N ไม่เกิน 1000 การหาปมร่วมไม่ต้องใช้ท่าหรู ไต่ขึ้นตามพ่อทีละชั้นก็พอ
ถนนแต่ละเส้นไต่ไม่เกิน N ชั้น ทั้งการหาปมร่วมและการรวม dp ตามเส้นทาง
| ช่วงในโค้ด | ทำอะไร | อ่านคู่กับ |
|---|---|---|
bfsTree | ปักรากที่เมือง 1 จดพ่อ ความลึก และลำดับของลูกแต่ละคน (เป็นตำแหน่งบิต) | ตอนที่ 5 |
lcaOf | ไต่ขึ้นทีละชั้นจนพบปมร่วมที่ต่ำที่สุด | ตอนที่ 5 |
len%2==1 | ทิ้งถนนที่เส้นทางยาวคี่ (ปิดเสมอ) | ตอนที่ 2 |
exAt[l] | แขวนถนนไว้ที่ปมร่วมของมัน | ตอนที่ 5 |
pathSum | รวม dp ของเมืองบนเส้นทาง แต่ละเมืองจองลูกที่เส้นทางเพิ่งขึ้นมา | ตอนที่ 5 |
สองลูปของ g | เติมแถว dp[v] ไล่ mask จากใหญ่ไปเล็ก กฎที่ 2 ซ่อนอยู่ในการเช็กว่าบิตไม่ชน | ตอนที่ 3, 5 |
total - dp[1][0] | ค่าปิดทั้งหมดลบน้ำหนักที่เก็บได้ | ตอนที่ 3 |
เวลาวัดบนเครื่องที่ใช้เขียน รวมเวลาเปิดโปรแกรมแล้ว ยังไม่ได้ส่งเข้าเครื่องตรวจของ programming.in.th
สามข้อนี้ตอบได้จากสิ่งที่อ่านมา ลองคิดก่อนแล้วค่อยเปิดคำตอบ
ข้อ 1. ถ้าโจทย์กลับด้าน ให้ปิดถนนจนไม่เหลือวงที่ยาวคี่แทน ต้องใช้ดีพีไหม
ข้อ 2. ทำไมถนนแต่ละเส้นถูกตัดสินที่ปมร่วมที่ต่ำที่สุด ไม่ใช่ที่ปลายทางของมัน
ข้อ 3. ทำไมในแต่ละเมืองต้องไล่ mask จากใหญ่ไปเล็ก
สองกฎในเฉลยเป็นข้อสรุปเรื่องคู่คี่ (parity) ทั้งคู่ เราไม่ได้สนใจว่าเส้นทางยาวเท่าไร สนใจแค่ว่ายาวคู่หรือคี่ พอถามแค่นั้น ถนนทั้งกราฟก็แบ่งเป็นสองพวกทันที
โจทย์ฝาแฝดที่ห้ามวงคี่แทน ตอบได้ด้วยการระบายสองสีตรง ๆ (ข้อ 1 ข้างบน) ส่วนข้อนี้ห้ามวงคู่ จึงต้องมีกฎที่ 2 กับดีพีเพิ่ม ถ้าอยากเห็นการใช้คู่คี่อีกแบบ ดูโจทย์เครื่องเคลื่อนย้ายมวลสาร
อีกท่าที่ข้อนี้ใช้คือการบีบสถานะ (state compression) ตอนไล่ต้นไม้ เราไม่เก็บทุกอย่างของกิ่ง เก็บแค่ค่าที่พ่อต้องใช้ ฝั่งต้นไม้มีบทปูพื้นใน DP บนต้นไม้ ฝั่งการเก็บเซตไว้ในบิตมีใน DP ที่สถานะคือความจำล่าสุด
ต้นไม้ทำให้ถนนนอกต้นไม้หนึ่งเส้นเท่ากับวงหนึ่งวง ความเป็นคู่เป็นคี่จึงตัดเส้นครึ่งหนึ่งทิ้งไปเลย และเงื่อนไขที่เหลือคือเส้นทางห้ามทับกัน ซึ่งพอดีกับดีพีบนต้นไม้ที่จำว่าลูกคนไหนถูกจองไปแล้ว
ในหน้านี้