อัลกอริทึมการวางระเบิด


212

ฉันมีn x mเมทริกซ์ที่ประกอบด้วยจำนวนเต็มไม่เป็นลบ ตัวอย่างเช่น:

2 3 4 7 1
1 5 2 6 2
4 3 4 2 1
2 1 2 4 1
3 1 3 4 1
2 1 4 3 2
6 9 1 6 4

"การทิ้งระเบิด" ลดจำนวนหนึ่งของเซลล์เป้าหมายและเพื่อนบ้านทั้งแปดของมันลงเหลือศูนย์ให้น้อยที่สุด

x x x 
x X x
x x x

อัลกอริธึมที่จะกำหนดจำนวนระเบิดขั้นต่ำที่ต้องการเพื่อลดเซลล์ทั้งหมดให้เป็นศูนย์คืออะไร

ตัวเลือก B (เนื่องจากฉันไม่ได้เป็นผู้อ่านอย่างระมัดระวัง)

อันที่จริงปัญหารุ่นแรกไม่ใช่ที่ฉันต้องการคำตอบ ฉันไม่ได้อ่านงานทั้งหมดอย่างระมัดระวังมีข้อ จำกัด เพิ่มเติมให้เราพูดว่า:

ปัญหาเกี่ยวกับอะไรง่าย ๆ เมื่อลำดับในแถวต้องไม่เพิ่มขึ้น:

8 7 6 6 5 เป็นไปได้ลำดับการป้อนข้อมูล

7 8 5 5 2 เป็นไปไม่ได้ตั้งแต่ 7 -> 8 ที่เพิ่มขึ้นในลำดับ

บางทีการหาคำตอบสำหรับกรณีที่ "ง่ายขึ้น" จะช่วยในการหาคำตอบที่ยากขึ้น

ป.ล. :ฉันเชื่อว่าเมื่อเรามีสถานการณ์เดียวกันหลายอย่างต้องใช้ระเบิดน้อยที่สุดเพื่อล้างบรรทัดบนเราเลือกหนึ่งที่ใช้ระเบิดส่วนใหญ่ทางด้านซ้ายของแถว ยังมีข้อพิสูจน์ใด ๆ ที่อาจถูกต้อง?


4
ฉันเพิ่งพบว่า ot บางฟิลด์สามารถข้ามได้ในตัวอย่างที่ 2 3 1 5 การวางมันลงบน 2,3,1 นั้นไม่มีจุดหมายเพราะการทิ้งมันทำให้เกิดความเสียหายบางส่วนซึ่งเราสามารถทำได้โดยการทิ้งที่ 5 แต่ไม่สามารถทำได้ ค้นหาวิธีทำให้มันใช้งานได้ทั่วโลก (หากเป็นวิธีที่ถูกต้อง) การล้าง 2 จำเป็นต้องใช้การทิ้งระเบิด 2 อันในเพื่อนบ้านและ 5 หน่วยมีความเสียหายอื่น ๆ แต่แล้วฉันก็ไม่รู้ว่าจะต้องทำอะไรต่อไปตั้งแต่เมื่อคุณเขียนใหม่ (หลังจากลดลง) คุณมีสองทางเลือก (ไม่มีความเสียหายเพียงชุดเดียว)
abc

23
นี่เป็น NP-hard หรือเปล่า? มันดูเหมือนจะเป็นตัวแปรของปัญหาความคุ้มครองสูงสุด
Mysticial

14
+1 ที่ให้สิ่งที่น่าสนใจแก่ฉัน
Nick Mitchinson

3
@ Kostek ปัญหาที่ยอดเยี่ยม! กรุณาโพสต์ลิงค์
พันเอก Panic

5
บางทีคุณควรจะอธิบายให้กระจ่างคุณพูดว่าคำถามคือ: what's the minimum amount of bombs required to clean the board?นี่หมายความว่าไม่จำเป็นต้องหารูปแบบการวางระเบิดจริงหรือ แต่เป็นจำนวนระเบิดน้อยที่สุด?
โกหกไรอัน

คำตอบ:


38

มีวิธีลดสิ่งนี้ให้เป็นปัญหาย่อยแบบง่าย

คำอธิบายนั้นมี 2 ส่วนส่วนอัลกอริทึมและเหตุผลที่อัลกอริธึมให้ทางออกที่ดีที่สุด คนแรกจะไม่เข้าท่าถ้าไม่มีคนที่สองดังนั้นฉันจะเริ่มด้วยเหตุผลว่าทำไม

หากคุณคิดว่าจะวางระเบิดรูปสี่เหลี่ยมผืนผ้า (ถือว่าเป็นรูปสี่เหลี่ยมผืนผ้าขนาดใหญ่ - ยังไม่มีกล่องขอบ) คุณจะเห็นได้ว่าวิธีเดียวที่จะลดรูปสี่เหลี่ยมกลวงของสี่เหลี่ยมบนขอบเขตให้เป็น 0 คือการระเบิดทั้งปริมณฑลหรือระเบิดสี่เหลี่ยมผืนผ้ากลวงของ กำลังสองภายในขอบเขต ฉันจะเรียกชั้นปริมณฑล 1 และสี่เหลี่ยมผืนผ้าด้านในนั้นชั้น 2

ความเข้าใจที่สำคัญคือไม่มีชั้นวางระเบิดจุดที่ 1 เพราะ "รัศมีการระเบิด" ที่คุณได้รับจากการทำเช่นนั้นจะถูกบรรจุไว้ในรัศมีการระเบิดของจตุรัสอื่นจากชั้น 2 เสมอคุณควรสามารถโน้มน้าวใจตัวเองได้อย่างง่ายดาย

ดังนั้นเราสามารถลดปัญหาในการค้นหาวิธีที่ดีที่สุดในการทิ้งปริมณฑลจากนั้นเราสามารถทำซ้ำได้จนกระทั่งสี่เหลี่ยมทั้งหมดเป็น 0

แต่แน่นอนว่าจะไม่พบทางออกที่ดีที่สุดเสมอไปหากมีความเป็นไปได้ที่จะทิ้งระเบิดในขอบเขตที่น้อยกว่าแฟชั่นที่ดีที่สุด แต่ด้วยการใช้ X ระเบิดพิเศษทำให้ปัญหาของการลดชั้นในง่ายกว่าโดย> ระเบิด X ดังนั้นถ้าเราเรียกชั้นผู้อนุญาตถ้าเราวางระเบิด X พิเศษไว้ที่ชั้น 2 (แค่ในชั้นที่ 1) เราสามารถลดความพยายามในการวางระเบิดชั้นที่ 2 ในภายหลังได้มากกว่า X หรือไม่? กล่าวอีกนัยหนึ่งเราต้องพิสูจน์ว่าเราสามารถโลภในการลดขอบเขตรอบนอก

แต่เรารู้ว่าเราสามารถโลภได้ เนื่องจากไม่มีระเบิดในชั้นที่ 2 จะมีประสิทธิภาพมากขึ้นในการลดเลเยอร์ 2 ถึง 0 มากกว่าวางระเบิดอย่างมีกลยุทธ์ในชั้นที่ 3 และด้วยเหตุผลเดียวกับก่อน - มีระเบิดเสมอที่เราสามารถวางในชั้นที่ 3 ที่จะส่งผลกระทบต่อทุกตาราง ของชั้น 2 ที่วางระเบิดในชั้นที่ 2 ได้ ดังนั้นจึงไม่สามารถทำร้ายเราให้โลภได้ (ในแง่นี้โลภ)

ดังนั้นสิ่งที่เราต้องทำคือหาวิธีที่ดีที่สุดในการลดผู้อนุญาตให้เหลือ 0 โดยการระเบิดชั้นในถัดไป

เราไม่เคยได้รับบาดเจ็บจากการทิ้งระเบิดที่มุมแรกเป็น 0 เพราะมีเพียงมุมของชั้นในเท่านั้นที่สามารถเข้าถึงได้ดังนั้นเราจึงไม่มีทางเลือก (และการวางระเบิดในขอบเขตที่สามารถเข้าถึงมุมได้นั้นมีรัศมีการระเบิดอยู่ใน รัศมีการระเบิดจากมุมของชั้นใน)

เมื่อเราทำเช่นนั้นแล้วสี่เหลี่ยมบนขอบเขตที่อยู่ติดกับมุม 0 จะสามารถเข้าถึงได้โดย 2 สี่เหลี่ยมจากชั้นใน:

0       A       B

C       X       Y

D       Z

เมื่อมาถึงจุดนี้ปริมณฑลจะมีประสิทธิภาพเป็น 1 มิติวงปิดเพราะระเบิดใด ๆ จะลด 3 สี่เหลี่ยมที่อยู่ติดกัน ยกเว้นบางสิ่งที่แปลกประหลาดใกล้มุม - X สามารถ "กด" A, B, C และ D

ตอนนี้เราไม่สามารถใช้กลอุบายการระเบิดได้ - สถานการณ์ของแต่ละช่องนั้นสมมาตรยกเว้นมุมแปลก ๆ และถึงแม้ว่าจะไม่มีรัศมีการระเบิดเป็นส่วนย่อยของอีกมุม โปรดทราบว่าถ้านี่เป็นเส้น (ขณะที่พันเอก Panic พูด) แทนที่จะเป็นวงปิดการแก้ปัญหาเล็กน้อย จุดสิ้นสุดจะต้องลดลงเป็น 0 และจะไม่เป็นอันตรายต่อคุณในการวางระเบิดจุดที่อยู่ติดกับจุดสิ้นสุดอีกครั้งเนื่องจากรัศมีการระเบิดเป็นระดับซูเปอร์เซ็ต เมื่อคุณสร้างจุดสิ้นสุด 0 ของคุณแล้วคุณยังคงมีจุดสิ้นสุดใหม่ดังนั้นให้ทำซ้ำ (จนกระทั่งบรรทัดเป็น 0 ทั้งหมด)

ดังนั้นถ้าเราสามารถลดสี่เหลี่ยมจัตุรัสเดียวในเลเยอร์เป็น 0 ได้อย่างเหมาะสมเรามีอัลกอริทึม (เพราะเราตัดลูปและตอนนี้มีเส้นตรงที่มีจุดสิ้นสุด) ฉันเชื่อว่าการวางระเบิดที่อยู่ติดกับจตุรัสที่มีค่าต่ำสุด (ให้คุณมี 2 ตัวเลือก) ดังนั้นค่าสูงสุดที่อยู่ภายใน 2 กำลังสองของค่าต่ำสุดนั้นคือค่าต่ำสุดที่เป็นไปได้ (คุณอาจต้องแยกการทิ้งระเบิดเพื่อจัดการสิ่งนี้) ยังไม่มีหลักฐานเลย


+1 - ฉันจะเขียนสิ่งที่คล้ายกัน ฉันคิดว่าคุณได้รับมัน!
Rex Kerr

5
@ คนทำขนมปังโปรดอ่านปัญหาให้ละเอียด การทิ้งระเบิดเป็นรูปสี่เหลี่ยมจัตุรัสจะช่วยลดเพื่อนบ้านทั้งแปดของมันดังนั้นการสันนิษฐานของเขาจึงถูกต้อง
darksky

20
But, we do know we can be greedy...- ฉันไม่ได้ซื้อมัน พิจารณา1 1 2 1 1 2ในขอบเขต จำนวนระเบิดขั้นต่ำคือ 4 แต่มีวิธีแก้ไขสามวิธีที่แตกต่างกัน แต่ละวิธีมีผลกระทบที่แตกต่างกันในชั้นถัดไป ตราบใดที่มีการแก้ปัญหาน้อยที่สุดสำหรับปริมณฑลคุณไม่สามารถแยกปริมณฑลได้อย่างสมบูรณ์โดยไม่คำนึงถึงชั้นใน ฉันไม่คิดว่าปัญหานี้สามารถแก้ไขได้โดยไม่ต้องย้อนรอย
user1354557

4
ฉันกำลังคิดเกี่ยวกับวิธีแก้ปัญหานี้ แต่มันดูง่ายมาก เป็นความจริงที่ว่าคุณสามารถวางระเบิดที่ชั้น 2 เพื่อทำความสะอาดชั้นที่ 1 แต่ถ้ามีวิธีแก้ปัญหาหลายอย่าง
Luka Rahne

12
@psr: มันไม่ทำงาน วิธีการวางระเบิดที่เหมาะสมที่สุดสำหรับชั้นนอกอาจไม่เหมาะสมที่สุดในระดับโลก ตัวอย่าง: 0011100 0100010 0000000 0000000 1110111. วิธีที่ดีที่สุดในการวางระเบิดเลเยอร์แรกคือการวางระเบิดที่แถวกลางของแถวที่สองโดยรวมการวางระเบิดจำนวนสามลูกเพื่อฆ่าชั้นนอก แต่คุณต้องใช้ลูกระเบิดสองลูกเพื่อดูแลเลเยอร์ถัดไป ความเหมาะสมที่สุดต้องการเพียงสี่ลูกระเบิดทั้งหมด: สองลูกสำหรับสองแถวแรกและสองลูกสำหรับแถวสุดท้าย
nneonneo

26

Pólyaกล่าวว่า "หากคุณไม่สามารถแก้ปัญหาได้แล้วมีปัญหาที่คุณสามารถแก้ไขได้ง่ายกว่า: หาได้"

ปัญหาที่ชัดเจนง่ายกว่าคือปัญหา 1 มิติ (เมื่อกริดเป็นแถวเดียว) เริ่มจากอัลกอริธึมที่ง่ายที่สุด - โละระเบิดเป้าหมายที่ใหญ่ที่สุด สิ่งนี้จะผิดพลาดเมื่อใด

ให้1 1 1อัลกอริทึมโลภไม่แยแสกับเซลล์ที่มันระเบิดก่อน แน่นอนเซลล์ตรงกลางดีกว่า - มันทั้งสามศูนย์พร้อมกัน นี่เป็นการแนะนำอัลกอริธึมใหม่ A "วางระเบิดเพื่อย่อจำนวนที่เหลือ" อัลกอริทึมนี้ผิดพลาดเมื่อใด

ให้1 1 2 1 1อัลกอริทึม A ไม่แยแสระหว่างการวางระเบิดเซลล์ที่ 2, 3 หรือ 4 แต่การทิ้งระเบิดเซลล์ที่สองเพื่อออกไป0 0 1 1 1นั้นดีกว่าการทิ้งระเบิดเซลล์ที่สองที่จะออก1 0 1 0 1ที่จะออกจาก จะแก้ไขได้อย่างไร? ปัญหาเกี่ยวกับการทิ้งระเบิดเซลล์ที่ 3 คือมันปล่อยให้เราทำงานไปทางซ้ายและทำงานไปทางขวาซึ่งจะต้องทำแยกต่างหาก

วิธีการ "วางระเบิดเพื่อลดจำนวนที่เหลืออยู่ให้เหลือน้อยที่สุด แต่เพิ่มขนาดต่ำสุดไปทางซ้าย (จากจุดที่เราทิ้งระเบิด) บวกกับต่ำสุดไปทางขวา" เรียกอัลกอริทึมนี้ B. เมื่ออัลกอริทึมนี้ผิดปกติ


แก้ไข: หลังจากอ่านความคิดเห็นแล้วฉันเห็นด้วยกับปัญหาที่น่าสนใจมากกว่านี้ก็คือปัญหามิติเดียวที่เปลี่ยนไปเพื่อให้ปลายเข้าร่วม ชอบที่จะเห็นความคืบหน้าใด ๆ ที่


40
ฉันไม่แน่ใจว่าทำไมคำตอบนี้ได้รับ upvotes มากมาย - คดี 1D เกือบจะไร้สาระเพียงแค่วางระเบิดองค์ประกอบทางด้านขวาขององค์ประกอบบวกแรก วิธีนี้ใช้ได้ผลเพราะมีวิธีที่ดีที่สุดวิธีหนึ่งเสมอในการวางระเบิดองค์ประกอบใด ๆ ที่มี 0 ของทางซ้าย นี่สามารถขยายเป็น 2D เพื่อลบสี่เหลี่ยมมุมได้อย่างเหมาะสม แต่ฉันไม่เห็นวิธีที่ชัดเจนในการขยายมันเกินกว่านั้น ... ?
BlueRaja - Danny Pflughoeft

3
@ BlueRaja ฉัน upvoting เพราะเห็นได้ชัดว่าแนวทางโลภที่กล่าวถึงในคำตอบอื่น ๆ ไม่เพียงพอ (อย่างน้อยก็ต้องได้รับการเสริมด้วยเกณฑ์เพิ่มเติม) ตัวเลือกเป้าหมายบางอย่างแม้ว่าจะทำให้จำนวนรวมลดลงเท่ากัน แต่ก็อาจทำให้สิ่งต่าง ๆ กระจายออกไปได้ดีกว่าตัวเลือกอื่น ๆ ฉันคิดว่านี่เป็นข้อมูลเชิงลึกที่เป็นประโยชน์สำหรับปัญหา 2D
ทิมกู๊ดแมน

3
และโดยทั่วไป "หากคุณติดอยู่กับตัวเรือน 2D ลองใช้ตัวเรือน 1D ก่อน" เป็นคำแนะนำที่ดี
ทิมกู๊ดแมน

21
@Tim: "'ลอง 1D กรณีแรกคือคำแนะนำที่ดี"ใช่มันซึ่งจะทำให้มันเป็นความคิดเห็นที่ยอดเยี่ยม; แต่มันไม่ใช่คำตอบ ...
BlueRaja - Danny Pflughoeft

3
ฉันคิดว่าคุณมีจุดที่ดีแม้ว่ากรณี 1D อาจจะทำให้เข้าใจผิดเล็กน้อยที่นี่เพราะมันมีวิธีง่ายๆที่ไม่ขยายไปถึงมิติที่สูงขึ้น ฉันคิดว่าคดี 1D ที่มีเงื่อนไขขอบเขตเป็นระยะ (การล้อมกรณี) อาจดีกว่า
ทิมกู๊ดแมน

12

ฉันต้องหยุดเพียงวิธีแก้ปัญหาบางส่วนตั้งแต่ฉันหมดเวลา แต่หวังว่าแม้วิธีแก้ปัญหาบางส่วนนี้จะให้ข้อมูลเชิงลึกเกี่ยวกับวิธีการที่เป็นไปได้วิธีหนึ่งในการแก้ปัญหานี้

เมื่อต้องเผชิญกับปัญหาที่ยากฉันชอบที่จะเกิดปัญหาง่ายขึ้นเพื่อพัฒนาสัญชาตญาณเกี่ยวกับพื้นที่ปัญหา ที่นี่ขั้นตอนแรกที่ฉันทำคือลดปัญหา 2 มิตินี้เป็นปัญหา 1 มิติ พิจารณาบรรทัด:

0 4 2 1 3 0 1

อย่างใดหรืออื่นคุณรู้ว่าคุณจะต้องมีการวางระเบิดหรือรอบ ๆ4จุดที่ 4 ครั้งที่จะได้รับมันลงไปตั้งแต่ 0. ด้านซ้ายของจุดที่เป็นตัวเลขที่ลดลงมีผลประโยชน์ที่จะระเบิด0หรือมากกว่าระเบิด4 2ในความเป็นจริงฉันเชื่อว่า (แต่ไม่มีหลักฐานที่เข้มงวด) ที่วางระเบิด2จนกระทั่ง4จุดลงไปที่ 0 อย่างน้อยก็ดีพอ ๆ กับกลยุทธ์อื่น ๆ เพื่อให้ได้4ถึง 0 เราสามารถลงบรรทัดจากซ้ายไปขวาในกลยุทธ์ แบบนี้:

index = 1
while index < line_length
  while number_at_index(index - 1) > 0
    bomb(index)
  end
  index++
end
# take care of the end of the line
while number_at_index(index - 1) > 0
  bomb(index - 1)
end

คำสั่งการวางระเบิดตัวอย่างคู่:

0 4[2]1 3 0 1
0 3[1]0 3 0 1
0 2[0]0 3 0 1
0 1[0]0 3 0 1
0 0 0 0 3[0]1
0 0 0 0 2[0]0
0 0 0 0 1[0]0
0 0 0 0 0 0 0

4[2]1 3 2 1 5
3[1]0 3 2 1 5
2[0]0 3 2 1 5
1[0]0 3 2 1 5
0 0 0 3[2]1 5
0 0 0 2[1]0 5
0 0 0 1[0]0 5
0 0 0 0 0 0[5]
0 0 0 0 0 0[4]
0 0 0 0 0 0[3]
0 0 0 0 0 0[2]
0 0 0 0 0 0[1]
0 0 0 0 0 0 0

แนวคิดของการเริ่มต้นด้วยตัวเลขที่ต้องลงไปไม่ทางใดก็ทางหนึ่งเป็นเรื่องที่น่าสนใจเพราะจู่ ๆ ก็สามารถหาทางแก้ไขที่บางคนอ้างว่าเป็นอย่างน้อยก็ดีเหมือนโซลูชันอื่น ๆ ทั้งหมด

ขั้นตอนต่อไปของความซับซ้อนที่การค้นหาอย่างน้อยก็ยังดีอยู่ที่ขอบกระดาน เห็นได้ชัดสำหรับฉันว่าไม่เคยมีประโยชน์อย่างเคร่งครัดในการวางระเบิดที่ขอบด้านนอก คุณดีกว่าที่จะทิ้งระเบิดจุดที่หนึ่งและรับอีกสามช่องว่างได้ฟรี เมื่อพิจารณาจากนี้เราสามารถพูดได้ว่าการระเบิดวงแหวนข้างในอย่างน้อยก็ดีเท่ากับการทิ้งขอบ ยิ่งไปกว่านั้นเราสามารถรวมสิ่งนี้เข้ากับสัญชาตญาณที่วางระเบิดด้านขวาของขอบนั้นเป็นวิธีเดียวที่จะทำให้ช่องว่างของขอบลดลงไปที่ 0 ยิ่งไปกว่านั้นมันง่ายมากที่จะหากลยุทธ์ที่ดีที่สุด อย่างน้อยก็ดีเท่ากลยุทธ์อื่น ๆ ) เพื่อลดจำนวนมุมลงเหลือ 0 เรารวมทั้งหมดเข้าด้วยกันและสามารถเข้าใกล้โซลูชันในพื้นที่ 2 มิติมากขึ้น

จากการสังเกตชิ้นส่วนมุมเราสามารถพูดได้อย่างมั่นใจว่าเรารู้กลยุทธ์ที่เหมาะสมในการเริ่มจากบอร์ดเริ่มต้นใด ๆ ไปยังกระดานที่มีเลขศูนย์ในทุกมุม นี่เป็นตัวอย่างของบอร์ดดังกล่าว (ฉันยืมตัวเลขจากกระดานเชิงเส้นทั้งสองด้านบน) ฉันระบุช่องว่างแตกต่างกันและฉันจะอธิบายว่าทำไม

0 4 2 1 3 0 1 0
4 x x x x x x 4
2 y y y y y y 2
1 y y y y y y 1
3 y y y y y y 3
2 y y y y y y 2
1 y y y y y y 1
5 y y y y y y 5
0 4 2 1 3 0 1 0

ใครจะสังเกตเห็นที่แถวด้านบนมันคล้ายกับตัวอย่างเชิงเส้นเราเห็นก่อนหน้า ระลึกถึงการสังเกตก่อนหน้านี้ของเราว่าวิธีที่ดีที่สุดในการทำให้แถวบนสุดทั้งหมดเป็น 0 คือการวางระเบิดแถวที่สอง ( xแถว) ไม่มีวิธีล้างแถวบนโดยการวางระเบิดแถวใด ๆyและไม่มีประโยชน์เพิ่มเติมในการวางระเบิดแถวบนสุดเหนือการวางระเบิดพื้นที่ที่สอดคล้องกันในxแถว

เราสามารถใช้กลยุทธ์เชิงเส้นจากด้านบน (วางระเบิดช่องว่างที่เกี่ยวข้องในxแถว) ที่เกี่ยวข้องกับตัวเราเองกับแถวด้านบนเท่านั้นและไม่มีอะไรอื่น มันจะเป็นอะไรเช่นนี้:

0 4 2 1 3 0 1 0
4 x[x]x x x x 4
2 y y y y y y 2
1 y y y y y y 1
3 y y y y y y 3
2 y y y y y y 2
1 y y y y y y 1
5 y y y y y y 5
0 4 2 1 3 0 1 0

0 3 1 0 3 0 1 0
4 x[x]x x x x 4
2 y y y y y y 2
1 y y y y y y 1
3 y y y y y y 3
2 y y y y y y 2
1 y y y y y y 1
5 y y y y y y 5
0 4 2 1 3 0 1 0

0 2 0 0 3 0 1 0
4 x[x]x x x x 4
2 y y y y y y 2
1 y y y y y y 1
3 y y y y y y 3
2 y y y y y y 2
1 y y y y y y 1
5 y y y y y y 5
0 4 2 1 3 0 1 0

0 1 0 0 3 0 1 0
4 x[x]x x x x 4
2 y y y y y y 2
1 y y y y y y 1
3 y y y y y y 3
2 y y y y y y 2
1 y y y y y y 1
5 y y y y y y 5
0 4 2 1 3 0 1 0

0 0 0 0 3 0 1 0
4 x x x x x x 4
2 y y y y y y 2
1 y y y y y y 1
3 y y y y y y 3
2 y y y y y y 2
1 y y y y y y 1
5 y y y y y y 5
0 4 2 1 3 0 1 0

ข้อบกพร่องในวิธีนี้จะชัดเจนมากในการวางระเบิดสองครั้งสุดท้าย เป็นที่ชัดเจนระบุว่าเว็บไซต์ระเบิดเท่านั้นที่ช่วยลด4ตัวเลขในคอลัมน์แรกในแถวที่สองเป็นครั้งแรกและx yการวางระเบิดสองครั้งสุดท้ายนั้นด้อยกว่าอย่างชัดเจนเพียงแค่วางระเบิดครั้งแรกxซึ่งจะทำแบบเดียวกันแน่นอน (โดยคำนึงถึงจุดแรกในแถวบนสุดซึ่งเราไม่มีวิธีการล้างแบบอื่น) เนื่องจากเราได้แสดงให้เห็นแล้วว่ากลยุทธ์ปัจจุบันของเราไม่ดีนักการปรับเปลี่ยนกลยุทธ์จึงเป็นสิ่งจำเป็นอย่างชัดเจน

ณ จุดนี้ฉันสามารถย้อนกลับไปในความซับซ้อนและมุ่งเน้นเพียงมุมเดียว ลองพิจารณาอันนี้กัน:

0 4 2 1
4 x y a
2 z . .
1 b . .

ก็เป็นที่ชัดเจนวิธีเดียวที่จะได้รับพื้นที่ที่มี4ลงไปที่ศูนย์จะระเบิดการรวมกันของบางx, และy zด้วยการแสดงผาดโผนบางอย่างในใจของฉันฉันค่อนข้างแน่ใจว่าทางออกที่ดีที่สุดคือการระเบิดxสามครั้งแล้วแล้วa bตอนนี้มันเป็นเรื่องของการหาวิธีที่ฉันไปถึงวิธีการแก้ปัญหานั้นและถ้ามันเผยให้เห็นสัญชาตญาณใด ๆ ที่เราสามารถใช้เพื่อแม้แต่แก้ปัญหาในท้องถิ่นนี้ ฉันสังเกตเห็นว่าไม่มีการทิ้งระเบิดyและzพื้นที่ ความพยายามที่จะหามุมที่การทิ้งระเบิดพื้นที่เหล่านั้นทำให้รู้สึกถึงมุมที่มีลักษณะเช่นนี้:

0 4 2 5 0
4 x y a .
2 z . . .
5 b . . .
0 . . . .

สำหรับอันนี้มันชัดเจนสำหรับฉันว่าทางออกที่ดีที่สุดคือการวางระเบิดy5 ครั้งและz5 ครั้ง ไปอีกขั้นหนึ่ง

0 4 2 5 6 0 0
4 x y a . . .
2 z . . . . .
5 b . . . . .
6 . . . . . .
0 . . . . . .
0 . . . . . .

ที่นี่รู้สึกคล้ายกับสัญชาตญาณว่าทางออกที่ดีที่สุดคือการทิ้งระเบิดaและb6 เท่าจากนั้นx4 ครั้ง

ตอนนี้มันกลายเป็นเกมของการเปลี่ยนสัญชาติญาณเหล่านั้นให้เป็นหลักการที่เราสามารถสร้างได้

หวังว่าจะดำเนินการต่อไป!


10

สำหรับคำถามที่อัปเดตอัลกอริทึมโลภอย่างง่าย ๆ ให้ผลลัพธ์ที่ดีที่สุด

ดร็อประเบิด [0,0] ไปที่เซลล์ A [1,1] จากนั้นทิ้งระเบิด [1,0] ไปยังเซลล์ A [2,1] และดำเนินการกระบวนการนี้ต่อไป ในการทำความสะอาดมุมซ้ายล่างให้ดรอปสูงสุด (A [N-1,0], A [N-2,0], A [N-3,0]) ไปยังเซลล์ A [N-2,1] สิ่งนี้จะทำความสะอาด 3 คอลัมน์แรกอย่างสมบูรณ์

ด้วยวิธีการที่สะอาดคอลัมน์เดียวกัน 3,4,5 จากนั้นคอลัมน์ 6,7,8 เป็นต้น

น่าเสียดายที่นี่ไม่ได้ช่วยแก้ปัญหาการค้นหาสำหรับปัญหาดั้งเดิม


ปัญหา "ที่ใหญ่กว่า" (โดยไม่มีข้อ จำกัด "ที่ไม่ใช่การลบ") อาจได้รับการพิสูจน์แล้วว่าเป็น NP-hard นี่คือภาพร่างหลักฐาน

สมมติว่าเรามีกราฟระนาบของระดับสูงสุด 3 ลองหาจุดสุดยอดขั้นต่ำสำหรับกราฟนี้ จากบทความของ Wikipedia ปัญหานี้เป็นปัญหาที่เกิดขึ้นยากสำหรับกราฟระนาบที่มีความยาวไม่เกิน 3 ซึ่งสามารถพิสูจน์ได้จากการลดลงของ Planar 3SAT และความแข็งของ Planar 3SAT - โดยลดลงจาก 3SAT หลักฐานทั้งสองนี้นำเสนอในการบรรยายเมื่อเร็ว ๆ นี้ใน"ขอบเขตล่างอัลกอริทึม"โดยศาสตราจารย์ Erik Demaine (การบรรยาย 7 และ 9)

หากเราแยกขอบบางส่วนของกราฟต้นฉบับ (กราฟซ้ายบนแผนภาพ) แต่ละอันมีจำนวนโหนดเพิ่มขึ้นเป็นคู่กราฟผลลัพธ์ (กราฟด้านขวาบนแผนภาพ) ควรมีจุดยอดต่ำสุดที่เหมือนกันสำหรับจุดยอดดั้งเดิม การแปลงดังกล่าวช่วยให้สามารถจัดตำแหน่งจุดยอดกราฟให้อยู่ในตำแหน่งที่กำหนดบนตาราง

ป้อนคำอธิบายรูปภาพที่นี่

หากเราวางกราฟจุดยอดให้กับแถวและคอลัมน์อย่างสม่ำเสมอ (ในลักษณะที่ไม่มีขอบสองจุดเกิดขึ้นที่จุดยอดหนึ่งจากมุมแหลม) ให้แทรก "อัน" ที่ใดก็ตามที่มีขอบและแทรก "ศูนย์" ไปยังตำแหน่งกริดอื่น เราสามารถใช้วิธีแก้ปัญหาใด ๆ สำหรับปัญหาดั้งเดิมเพื่อค้นหาจุดสุดยอดขั้นต่ำ


กราฟด้านซ้ายมาจากไหน ขออภัยฉันไม่เข้าใจคำอธิบายของคุณ!
ryyst

1
@ryyst: กราฟนั้นจากด้านซ้ายเป็นเพียงตัวอย่างของกราฟระนาบ มันถูกใช้เพื่อแสดงให้เห็นถึงวิธีการแปลงกราฟระนาบใด ๆ ของการศึกษาระดับปริญญาได้ถึง 4 เพื่อกราฟแนวตารางและจากนั้นไปที่เมทริกซ์ n * m อัลกอริธึม "การทิ้งระเบิด" ที่ใช้กับเมทริกซ์นี้จะช่วยแก้ปัญหาจุดสุดยอดสำหรับกราฟที่ถูกแปลงและดังนั้นสำหรับกราฟ "ซ้าย"
Evgeny Kluev

อ่าฉันเข้าใจแล้วและฉันเชื่อว่าการเปลี่ยนแปลงของคุณถูกต้อง ขอบคุณ!
ryyst

@EvgenyKluev ฉันคิดว่าตอนนี้คุณต้องพิสูจน์ว่าจุดสุดยอดยังคงเป็นปัญหาที่ยากสำหรับ "กราฟระนาบของระดับสูงสุด 4"
Shahbaz

@Shahbaz: ฉันกลัวว่าหลักฐานนี้จะยาวเกินไป ดังนั้นฉันจึงเพิ่มลิงก์ไปยังหลักฐาน
Evgeny Kluev

9

คุณสามารถแสดงถึงปัญหานี้เป็นปัญหาการเขียนโปรแกรมจำนวนเต็ม (นี่เป็นเพียงหนึ่งในวิธีการแก้ไขปัญหาที่เป็นไปได้ในการแก้ไขปัญหานี้)

มีคะแนน:

a b c d
e f g h
i j k l
m n o p

เราสามารถเขียนสมการ 16 จุดสำหรับจุดยกตัวอย่างเช่น

f <= ai + bi + ci + ei + fi + gi + ii + ji + ki   

มีผลกระทบน้อยที่สุดจากผลรวมของดัชนีและโซลูชันจำนวนเต็มทั้งหมด

การแก้ปัญหาคือผลรวมแน่นอนของดัชนีนี้

สิ่งนี้สามารถทำให้ง่ายขึ้นโดยการตั้งค่า xi ทั้งหมดในขอบเขต 0 ดังนั้นคุณจะได้สมการ 4 + 1 ในตัวอย่างนี้

ปัญหาคือไม่มี algorhitm เล็กน้อยสำหรับการแก้ปัญหาดังกล่าว ฉันไม่ใช่ผู้เชี่ยวชาญเกี่ยวกับเรื่องนี้ แต่การแก้ปัญหานี้เนื่องจากการโปรแกรมเชิงเส้นยากมาก


8
ปัญหาทั้งหมดใน NP สามารถกำหนดเป็นปัญหาการเขียนโปรแกรมจำนวนเต็มดังนั้นสิ่งนี้ไม่เป็นประโยชน์มากเว้นแต่ว่าเรารู้อยู่แล้วว่าปัญหาคือปัญหา NP-Complete
BlueRaja - Danny Pflughoeft

1
ฉันเห็นด้วย. นอกจากนี้ยังไม่จำเป็นต้องรู้การเคลื่อนไหวที่แน่นอนที่ต้องทำเพื่อที่จะรู้ว่าการแก้ปัญหาคืออะไร
Luka Rahne

1
เมื่อคุณตั้งค่าขอบเขตเป็น 0 จำนวนความไม่เท่าเทียมยังคงเป็น 16
darksky

9

นี่เป็นคำตอบบางส่วนฉันพยายามค้นหาขอบเขตล่างและขอบเขตบนที่อาจเป็นจำนวนระเบิดได้

ในกระดานขนาด 3x3 และขนาดเล็กกว่าการแก้ปัญหาถือเป็นหมายเลขที่ใหญ่ที่สุดเสมอ

ในบอร์ดที่มีขนาดใหญ่กว่า 4x4 ขอบเขตล่างแรกที่เห็นได้ชัดคือผลรวมของมุม:

*2* 3  7 *1*
 1  5  6  2
 2  1  3  2
*6* 9  6 *4*

อย่างไรก็ตามคุณจัดการกับระเบิดมันเป็นไปไม่ได้ที่จะล้างกระดาน 4x4 นี้ด้วยการระเบิดน้อยกว่า 2 + 1 + 6 + 4 = 13

มีการกล่าวถึงในคำตอบอื่น ๆ ว่าการวางระเบิดบนมุมที่สองเพื่อกำจัดมุมนั้นไม่เคยเลวร้ายไปกว่าการวางระเบิดไว้ที่มุมตัวเอง

*2* 3  4  7 *1*
 1  5  2  6  2
 4  3  4  2  1
 2  1  2  4  1
 3  1  3  4  1
 2  1  4  3  2
*6* 9  1  6 *4*

เราสามารถทำให้มุมเป็นศูนย์โดยการวางระเบิดบนมุมที่สองเพื่อให้กระดานใหม่:

 0  1  1  6  0
 0  3  0  5  1
 2  1  1  1  0
 2  1  2  4  1
 0  0  0  0  0
 0  0  0  0  0
 0  3  0  2  0

จนถึงตอนนี้ดีมาก เราต้องการ 13 ระเบิดเพื่อเคลียร์มุม

ตอนนี้สังเกตหมายเลข 6, 4, 3 และ 2 ที่ทำเครื่องหมายไว้ด้านล่าง:

 0  1  1 *6* 0
 0  3  0  5  1
 2  1  1  1  0
*2* 1  2 *4* 1
 0  0  0  0  0
 0  0  0  0  0
 0 *3* 0  2  0

ไม่มีทางที่จะทิ้งระเบิดสองอันเซลล์ที่ใช้ระเบิดเดี่ยวดังนั้นระเบิดขั้นต่ำเพิ่มขึ้น 6 + 4 + 3 + 2 ดังนั้นเพิ่มจำนวนระเบิดที่เราใช้เพื่อล้างมุม จำนวนระเบิดที่จำเป็นสำหรับแผนที่นี้ได้กลายเป็น 28 ระเบิด เป็นไปไม่ได้ที่จะล้างแผนที่นี้ด้วยการวางระเบิดน้อยกว่า 28 ครั้งนี่คือขอบเขตล่างของแผนที่นี้

คุณสามารถใช้อัลกอริทึมโลภเพื่อสร้างขอบเขตบน คำตอบอื่น ๆ แสดงให้เห็นว่าอัลกอริทึมโลภสร้างทางออกที่ใช้ระเบิด 28 ลูก เนื่องจากเราได้พิสูจน์มาแล้วก่อนหน้านี้ว่าไม่มีวิธีแก้ปัญหาที่ดีที่สุดที่จะมีระเบิดได้น้อยกว่า 28 ระเบิดดังนั้นจึงมี 28 ระเบิดเป็นทางออกที่ดีที่สุด

เมื่อโลภและวิธีการค้นหาขอบเขตที่น้อยที่สุดที่ฉันได้กล่าวถึงข้างต้นไม่ได้มาบรรจบกันฉันคิดว่าคุณต้องกลับไปตรวจสอบชุดค่าผสมทั้งหมด

อัลกอริทึมสำหรับการค้นหาขอบเขตล่างมีดังต่อไปนี้:

  1. เลือกองค์ประกอบที่มีจำนวนมากที่สุดตั้งชื่อเป็น P
  2. ทำเครื่องหมายเซลล์ทั้งหมดสองขั้นตอนจาก P และ P ตัวเองเป็น unpickable
  3. เพิ่ม P ลงในminimumsรายการ
  4. ทำซ้ำจนถึงขั้นตอนที่ 1 จนกว่าเซลล์ทั้งหมดจะไม่สามารถเลิกทำการได้
  5. รวมminimumsรายการเพื่อรับขอบเขตล่าง

9

นี่เป็นวิธีโลภ:

  1. คำนวณเมทริกซ์ "คะแนน" ของคำสั่ง n x m โดยที่คะแนน [i] [j] คือการหักคะแนนทั้งหมดในเมทริกซ์หากตำแหน่ง (i, j) ถูกระเบิด (คะแนนสูงสุดของจุดคือ 9 และคะแนนขั้นต่ำเป็น 0)

  2. ย้ายแถวอย่างชาญฉลาดค้นหาและเลือกตำแหน่งแรกที่มีคะแนนสูงสุด (พูด (i, j))

  3. ระเบิด (i, j) เพิ่มจำนวนระเบิด

  4. หากองค์ประกอบทั้งหมดของเมทริกซ์ดั้งเดิมไม่เป็นศูนย์ให้ไปที่ 1

ฉันมีข้อสงสัยว่านี่เป็นทางออกที่ดีที่สุด

แก้ไข:

วิธีการโลภที่ฉันโพสต์ข้างต้นในขณะที่ใช้งานได้ส่วนใหญ่อาจไม่ได้ให้ทางออกที่ดีที่สุดแก่เรา ดังนั้นฉันคิดว่าควรเพิ่มองค์ประกอบของ DP ลงไป

ฉันคิดว่าเราสามารถตกลงกันได้ทุกเวลาตำแหน่งใดตำแหน่งหนึ่งที่มีคะแนนสูงสุด (คะแนน [i] [j] = การหักคะแนนรวมถ้า (i, j) ถูกทิ้งระเบิด) จะต้องถูกกำหนดเป้าหมาย เริ่มจากสมมติฐานนี้ต่อไปนี้เป็นวิธีการใหม่:

NumOfBombs (M): (ส่งคืนจำนวนระเบิดขั้นต่ำที่ต้องการ)

  1. รับเมทริกซ์ M ของคำสั่ง n X m ถ้าองค์ประกอบทั้งหมดของ M เป็นศูนย์ให้ส่งคืน 0

  2. คำนวณเมทริกซ์ "คะแนน" M

    ปล่อยให้ตำแหน่งที่แตกต่าง k P1, P2, ... Pk (1 <= k <= n * m) เป็นตำแหน่งใน M ที่มีคะแนนสูงสุด

  3. ส่งคืน (1 + นาที (NumOfBombs (M1), NumOfBombs (M2), ... , NumOfBombs (Mk))

    โดยที่ M1, M2, ... , Mk เป็นเมทริกซ์ที่เกิดขึ้นถ้าเราวางตำแหน่ง P1, P2, ... , Pk ตามลำดับ

นอกจากนี้หากเราต้องการคำสั่งของตำแหน่งที่จะทำนุกนอกเหนือจากนี้เราจะต้องติดตามผลการ "นาที"


3
ฉันสงสัยว่าการตั้งค่าคะแนนเป็นผลรวมของค่าปัจจุบันจะให้ผลลัพธ์ที่ดีกว่าหรือไม่ ซึ่งจะทำให้พื้นเรียบขึ้นอย่างมีประสิทธิภาพ
Eugene

@Eugene: จุดที่น่าสนใจมาก ฉันไม่สามารถนึกถึงเหตุผลที่วิธีการของคุณไม่ควรให้ผลลัพธ์ที่ดีกว่า ...
SidR

@Eugene: บางทีผลรวมของค่าปัจจุบันในบริเวณใกล้เคียงสามารถใช้สำหรับการวัด "ลำดับความสำคัญ"? Nuke โหนดที่มีคะแนนสูงสุดและมีลำดับความสำคัญสูงสุด ..
SidR

เพิ่งอ่านคำตอบนี้ฉันคิดว่ามันคล้ายกับคำตอบที่สองที่ฉันเพิ่งโพสต์ (อาจสะกดคำตอบของฉันได้อีกเล็กน้อย) ฉันคิดว่ามันจะดีที่สุดถ้ามีพื้นที่เดียวที่มีคะแนนสูงสุดเสมอเพราะคุณจะมั่นใจได้ว่าการวางระเบิดทุกครั้งจะมีผลกระทบมากที่สุด เพื่อระเบิดไม่ได้เรื่องจึงจะมีหนึ่งที่ดีที่สุดในแต่ละขั้นตอนที่ควรจะเป็นที่ดีที่สุด แต่เนื่องจากอาจมีความสัมพันธ์กับ "ดีที่สุด" อาจจะเป็นทางออกที่ดีที่สุดคุณต้องย้อนกลับและลองทั้งคู่เมื่อมีการเสมอกัน
ทิมกู๊ดแมน

1
@Eugene บางทีฉันอาจไม่ได้ติดตามคุณ ความแตกต่างระหว่างการลดที่ใหญ่ที่สุดและผลรวมที่น้อยที่สุดของค่าที่เหลือทั้งหมดคืออะไร ผลรวมของมูลค่าที่เหลืออยู่ (หลังจากการทิ้งระเบิด) เป็นเพียงมูลค่ารวมปัจจุบันลบด้วยการลดลงจากการทิ้งระเบิดพื้นที่นั้นดังนั้นสิ่งเหล่านี้ไม่เทียบเท่ากัน?
ทิมกู๊ดแมน

8

ปัญหาใหม่ของคุณซึ่งมีค่าที่ไม่เพิ่มจำนวนข้ามแถวนั้นค่อนข้างง่ายในการแก้ไข

สังเกตว่าคอลัมน์ด้านซ้ายมีตัวเลขสูงสุด ดังนั้นโซลูชันที่ดีที่สุดใด ๆ จะต้องลดคอลัมน์นี้ลงเป็นศูนย์ก่อน ดังนั้นเราสามารถทำการวางระเบิดแบบ 1-Dบนคอลัมน์นี้ลดองค์ประกอบทุกอย่างในศูนย์ เราปล่อยให้ระเบิดตกในคอลัมน์ที่สองเพื่อให้พวกเขาได้รับความเสียหายสูงสุด มีโพสต์มากมายที่เกี่ยวข้องกับคดี 1D ฉันคิดว่าดังนั้นฉันรู้สึกปลอดภัยที่จะข้ามกรณีนี้ (ถ้าคุณต้องการให้ฉันอธิบายมันฉันทำได้) เนื่องจากคุณสมบัติที่ลดลงคอลัมน์ทั้งสามด้านซ้ายสุดจะถูกลดเหลือศูนย์ แต่เราจะใช้ระเบิดจำนวนน้อยที่สุดที่นี่เพราะคอลัมน์ด้านซ้ายจะต้องเป็นศูนย์

ทีนี้, เมื่อคอลัมน์ด้านซ้ายเป็นศูนย์, เราเพิ่งตัดคอลัมน์ซ้ายสุดสามคอลัมน์ที่เป็นศูนย์แล้วทำซ้ำกับเมทริกซ์ที่ลดลงในขณะนี้ สิ่งนี้จะต้องให้ทางออกที่ดีที่สุดแก่เราเพราะในแต่ละด่านเราใช้ระเบิดจำนวนน้อยที่สุด


ฉันเข้าใจแล้ว ฉันคิดถึงความคิดที่คล้ายกัน : S ครั้งต่อไปฉันจะอ่านให้ละเอียดยิ่งขึ้น แต่ต้องขอบคุณที่หลายคนมีปัญหา 'ดี' ที่จะแก้ปัญหา '
abc

4

Mathematica Integer Linear Programming โดยใช้สาขาและขอบเขต

ตามที่ได้รับการกล่าวถึงปัญหานี้สามารถแก้ไขได้โดยใช้การเขียนโปรแกรมเชิงเส้นจำนวนเต็ม (ซึ่งเป็นNP-Hard ) Mathematica มี ILP ในตัวอยู่แล้ว"To solve an integer linear programming problem Mathematica first solves the equational constraints, reducing the problem to one containing inequality constraints only. Then it uses lattice reduction techniques to put the inequality system in a simpler form. Finally, it solves the simplified optimization problem using a branch-and-bound method."[ดูการสอนการเพิ่มประสิทธิภาพข้อ จำกัดใน Mathematica]

ฉันได้เขียนรหัสต่อไปนี้ซึ่งใช้ไลบรารี ILP ของ Mathematica มันเร็วอย่างน่าประหลาดใจ

solveMatrixBombProblem[problem_, r_, c_] := 
 Module[{}, 
  bombEffect[x_, y_, m_, n_] := 
   Table[If[(i == x || i == x - 1 || i == x + 1) && (j == y || 
        j == y - 1 || j == y + 1), 1, 0], {i, 1, m}, {j, 1, n}];
  bombMatrix[m_, n_] := 
   Transpose[
    Table[Table[
      Part[bombEffect[(i - Mod[i, n])/n + 1, Mod[i, n] + 1, m, 
        n], (j - Mod[j, n])/n + 1, Mod[j, n] + 1], {j, 0, 
       m*n - 1}], {i, 0, m*n - 1}]];
  X := x /@ Range[c*r];
  sol = Minimize[{Total[X], 
     And @@ Thread[bombMatrix[r, c].X >= problem] && 
      And @@ Thread[X >= 0] && Total[X] <= 10^100 && 
      Element[X, Integers]}, X];
  Print["Minimum required bombs = ", sol[[1]]];
  Print["A possible solution = ", 
   MatrixForm[
    Table[x[c*i + j + 1] /. sol[[2]], {i, 0, r - 1}, {j, 0, 
      c - 1}]]];]

สำหรับตัวอย่างที่ให้ไว้ในปัญหา:

solveMatrixBombProblem[{2, 3, 4, 7, 1, 1, 5, 2, 6, 2, 4, 3, 4, 2, 1, 2, 1, 2, 4, 1, 3, 1, 3, 4, 1, 2, 1, 4, 3, 2, 6, 9, 1, 6, 4}, 7, 5]

เอาท์พุท

ป้อนคำอธิบายรูปภาพที่นี่

สำหรับทุกคนที่อ่านข้อความนี้ด้วยอัลกอริทึมโลภ

ลองใช้รหัสของคุณกับปัญหา 10x10 ต่อไปนี้:

5   20  7   1   9   8   19  16  11  3  
17  8   15  17  12  4   5   16  8   18  
4   19  12  11  9   7   4   15  14  6  
17  20  4   9   19  8   17  2   10  8  
3   9   10  13  8   9   12  12  6   18  
16  16  2   10  7   12  17  11  4   15  
11  1   15  1   5   11  3   12  8   3  
7   11  16  19  17  11  20  2   5   19  
5   18  2   17  7   14  19  11  1   6  
13  20  8   4   15  10  19  5   11  12

ที่นี่คั่นด้วยจุลภาค:

5, 20, 7, 1, 9, 8, 19, 16, 11, 3, 17, 8, 15, 17, 12, 4, 5, 16, 8, 18, 4, 19, 12, 11, 9, 7, 4, 15, 14, 6, 17, 20, 4, 9, 19, 8, 17, 2, 10, 8, 3, 9, 10, 13, 8, 9, 12, 12, 6, 18, 16, 16, 2, 10, 7, 12, 17, 11, 4, 15, 11, 1, 15, 1, 5, 11, 3, 12, 8, 3, 7, 11, 16, 19, 17, 11, 20, 2, 5, 19, 5, 18, 2, 17, 7, 14, 19, 11, 1, 6, 13, 20, 8, 4, 15, 10, 19, 5, 11, 12

สำหรับปัญหานี้ทางออกของฉันมี208ระเบิด นี่เป็นวิธีแก้ปัญหาที่เป็นไปได้ (ฉันสามารถแก้ไขได้ในเวลาประมาณ 12 วินาที)

ป้อนคำอธิบายรูปภาพที่นี่

เป็นวิธีการทดสอบผลลัพธ์ที่ Mathematica ผลิตดูว่าอัลกอริทึมโลภของคุณสามารถทำได้ดีกว่านี้หรือไม่


ฉันสามารถทำได้ใน 219 ด้วยคำตอบนี้: stackoverflow.com/questions/15300149/bomb-dropping-algorithm/…
Anthony Queen

3

ไม่จำเป็นต้องแปลงปัญหาเป็นปัญหาย่อยเชิงเส้น

แทนที่จะใช้ฮิวริสติกแบบโลภซึ่งเป็นการวางมุมโดยเริ่มจากจุดที่ใหญ่ที่สุด

ในตัวอย่างที่กำหนดมีสี่มุม {2, 1, 6, 4} สำหรับแต่ละมุมไม่มีการเคลื่อนไหวที่ดีไปกว่าการวางระเบิดในแนวทแยงของเซลล์ไปที่มุมดังนั้นเราจึงรู้ว่าการระเบิด 2 + 1 + 6 + 4 = 13 ครั้งแรกของเราจะต้องอยู่ในเซลล์แนวทแยงเหล่านี้ หลังจากทำการทิ้งระเบิดเราจะเหลือเมทริกซ์ใหม่:

2 3 4 7 1      0 1 1 6 0      0 1 1 6 0     1 1 6 0     0 0 5     0 0 0 
1 5 2 6 2      0 3 0 5 1      0 3 0 5 1  => 1 0 4 0  => 0 0 3  => 0 0 0  
4 3 4 2 1      2 1 1 1 0      2 1 1 1 0     0 0 0 0     0 0 0     0 0 3  
2 1 2 4 1  =>  2 1 2 4 1  =>  2 1 2 4 1     0 0 3 0     0 0 3      
3 1 3 4 1      0 0 0 0 0      0 0 0 0 0 
2 1 4 3 2      0 0 0 0 0      0 0 0 0 0 
6 9 1 6 4      0 3 0 2 0      0 0 0 0 0 

หลังจากการทิ้งระเบิด 13 ครั้งแรกเราใช้ฮิวริสติกเพื่อกำจัด 3 0 2 ผ่านการวางระเบิดสามครั้ง ตอนนี้เรามี 2 มุมใหม่ {2, 1} ในแถวที่ 4 เราทิ้งระเบิดอีกสามลูก เราได้ลดเมทริกซ์เป็น 4 x 4 ในขณะนี้ มีมุมหนึ่งมุมซ้ายบน เราวางระเบิดนั่น ตอนนี้เราเหลือ 2 มุมแล้ว {5, 3} เนื่องจาก 5 เป็นมุมที่ใหญ่ที่สุดที่เราวางระเบิดครั้งแรก, 5 ระเบิดจากนั้นก็ระเบิดที่มุมอีกมุมหนึ่ง รวมเป็น 13 + 3 + 3 + 1 + 5 + 3 = 28


1
ฉันไม่เข้าใจว่าคุณทำอะไรในกรณีทั่วไปหลังจากเกิดเหตุลอบวางระเบิด
RiaD

การวางระเบิดในมุมจะไม่มีประสิทธิภาพมากกว่าการทิ้งระเบิดในแนวทแยงมุมจากมุม
psr

1
psr คุณเข้าใจผิดโพสต์ของฉันฉันกำลังระเบิดในแนวทแยงมุมอ่านโพสต์อีกครั้ง
Tyler Durden

11
@TylerDurden: ใช้งานได้เพียงเพราะเมทริกซ์มีขนาดเล็ก สำหรับการฝึกอบรมที่ใหญ่กว่าหลังจากการทิ้งระเบิดที่มุมโดยทั่วไปแล้วคุณจะไม่สามารถตัดขอบอีกต่อไป
Lie Ryan

3

นี่เป็นการค้นหาที่กว้างที่สุดสำหรับเส้นทางที่สั้นที่สุด (ชุดระเบิด) ผ่าน "เขาวงกต" ของตำแหน่งนี้ ไม่ฉันไม่สามารถพิสูจน์ได้ว่าไม่มีอัลกอริทึมเร็วกว่าขออภัย

#!/usr/bin/env python

M = ((1,2,3,4),
     (2,3,4,5),
     (5,2,7,4),
     (2,3,5,8))

def eachPossibleMove(m):
  for y in range(1, len(m)-1):
    for x in range(1, len(m[0])-1):
      if (0 == m[y-1][x-1] == m[y-1][x] == m[y-1][x+1] ==
               m[y][x-1]   == m[y][x]   == m[y][x+1] ==
               m[y+1][x-1] == m[y+1][x] == m[y+1][x+1]):
        continue
      yield x, y

def bomb(m, (mx, my)):
  return tuple(tuple(max(0, m[y][x]-1)
      if mx-1 <= x <= mx+1 and my-1 <= y <= my+1
      else m[y][x]
      for x in range(len(m[y])))
    for y in range(len(m)))

def findFirstSolution(m, path=[]):
#  print path
#  print m
  if sum(map(sum, m)) == 0:  # empty?
    return path
  for move in eachPossibleMove(m):
    return findFirstSolution(bomb(m, move), path + [ move ])

def findShortestSolution(m):
  black = {}
  nextWhite = { m: [] }
  while nextWhite:
    white = nextWhite
    nextWhite = {}
    for position, path in white.iteritems():
      for move in eachPossibleMove(position):
        nextPosition = bomb(position, move)
        nextPath = path + [ move ]
        if sum(map(sum, nextPosition)) == 0:  # empty?
          return nextPath
        if nextPosition in black or nextPosition in white:
          continue  # ignore, found that one before
        nextWhite[nextPosition] = nextPath

def main(argv):
  if argv[1] == 'first':
    print findFirstSolution(M)
  elif argv[1] == 'shortest':
    print findShortestSolution(M)
  else:
    raise NotImplementedError(argv[1])

if __name__ == '__main__':
  import sys
  sys.exit(main(sys.argv))

1
อัลกอริทึมนี้จะค้นหาจำนวนการเคลื่อนไหวน้อยที่สุด แต่อาจใช้เวลานานมาก คุณรันสิ่งนี้ในชุดข้อมูลที่ให้มาหรือไม่? ที่จะให้พื้นฐานสำหรับอัลกอริทึมอื่น ๆ เพื่อเปรียบเทียบกับ
Ryan Amos

1
เซตย่อยของเมทริกซ์ที่กำหนด 5x4 ได้รับการแก้ไขในเวลาประมาณ 2 วินาที 5x5 ใช้เวลาไปแล้ว 2 นาที ฉันยังไม่ได้ลองอีก ;-) ใช่อัลกอริทึมนี้ยังไม่ได้รับการปรับให้เหมาะกับอะไรนอกจากงานดั้งเดิม: ค้นหาวิธีแก้ปัญหาที่สั้นที่สุด
Alfe

2
นั่นคือความงามของความซับซ้อนที่อธิบาย
Ryan Amos

3

ดูเหมือนว่าวิธีการเขียนโปรแกรมเชิงเส้นจะมีประโยชน์มากที่นี่

ให้P m xnเป็นเมทริกซ์ที่มีค่าของตำแหน่ง:

เมทริกซ์ของตำแหน่ง

ตอนนี้ให้นิยามเมทริกซ์ระเบิด B (x, y) mxn , ด้วย1 ≤ x ≤ m , 1 ≤ y ≤ nดังนี้

เมทริกซ์ระเบิด

ในลักษณะที่

ค่าของตำแหน่งในเมทริกซ์ระเบิด

ตัวอย่างเช่น:

B (3, 3)

เรากำลังหาเมทริกซ์B m xn = [ b ij ] นั่น

  1. สามารถกำหนดเป็นผลรวมของเมทริกซ์ระเบิด:

    B เป็นผลรวมของเมทริกซ์ระเบิด

    ( q ijจะเท่ากับปริมาณของระเบิดที่เราจะวางในตำแหน่งp ij )

  2. p ij - b ij ≤ 0 (หากต้องการความช่วยเหลือมากกว่านี้ขอให้เราพูดเป็นP - B ≤ 0 )

นอกจากนี้Bผลรวมของปริมาณของระเบิดควรลดทุน

เราสามารถเขียนBเป็นเมทริกซ์ที่น่าเกลียดข้างหน้า:

B เป็นเมทริกซ์ของผลรวมของปริมาณ

และเนื่องจากP - B ≤ 0 (ซึ่งหมายถึงP ≤ B ) เรามีระบบความไม่เท่าเทียมกันเชิงเส้นดังต่อไปนี้:

ความสัมพันธ์ระหว่างจำนวนระเบิดลดลงและค่าในตำแหน่ง

เป็นq mn x 1หมายถึง

ปริมาณของเวกเตอร์

p mn x 1หมายถึง

ค่าของ P กระจายเป็นเวกเตอร์

เราสามารถพูดได้ว่าเรามีระบบระบบด้านล่างแสดงเป็นผลิตภัณฑ์ของเมทริกซ์ http://latex.codecogs.com/gif.download?S%5Cmathbf%7Bq%7D&space;%5Cge&space;%5Cmathbf%7Bp%7D being S mn x mnเมทริกซ์ที่จะกลับด้านเพื่อแก้ปัญหาระบบ ฉันไม่ได้ขยายมันด้วยตัวเอง แต่ฉันเชื่อว่ามันควรจะง่ายในการเขียนโค้ด

ตอนนี้เรามีปัญหาขั้นต่ำซึ่งสามารถกล่าวได้ว่า

ระบบที่เราต้องแก้ไข

ฉันเชื่อว่ามันเป็นสิ่งที่ง่ายเกือบจะไม่สำคัญที่จะแก้ไขด้วยบางสิ่งบางอย่างเช่นอัลกอริธึม simplex (มีเอกสารนี้ค่อนข้างดีเกี่ยวกับเรื่องนี้ ) อย่างไรก็ตามฉันรู้ว่าแทบจะไม่มีโปรแกรมเชิงเส้น (ฉันจะเรียนเกี่ยวกับเรื่องนี้ใน Coursera แต่มันเป็นเพียงในอนาคต ... ) ฉันมีอาการปวดหัวบางอย่างพยายามที่จะเข้าใจมันและฉันมีงานอิสระขนาดใหญ่ที่จะเสร็จดังนั้นฉัน เพียงแค่ยอมแพ้ที่นี่ มันอาจจะเป็นว่าผมทำอะไรผิดในบางจุดหรือว่ามันไม่สามารถไปเพิ่มเติมใด ๆ แต่ผมเชื่อว่าเส้นทางนี้ในที่สุดจะนำไปสู่การวิธีการแก้ปัญหา อย่างไรก็ตามฉันกังวลสำหรับความคิดเห็นของคุณ

(ขอขอบคุณเป็นพิเศษสำหรับเว็บไซต์ที่น่าทึ่งนี้ในการสร้างภาพจากการแสดงออกของ LaTeX )


คุณแน่ใจหรือว่าความไม่เท่าเทียมของคุณไม่ถูกย้อนกลับ? นั่นคือ Sq> = P? นั่นคือจำนวนครั้งทั้งหมดที่สี่เหลี่ยมจัตุรัสถูกวางระเบิดมากกว่าหรือเท่ากับเมทริกซ์ที่กำหนด
darksky

1
เมื่อตัวแปรของโปรแกรมเชิงเส้นถูก จำกัด ให้เป็นจำนวนเต็มเราเรียกว่า"การโปรแกรมเชิงเส้นจำนวนเต็ม" (IP) ซึ่งแตกต่างจากกรณีอย่างต่อเนื่อง IP คือ NP-Complete น่าเสียดายที่อัลกอริทึมแบบซิมเพล็กซ์ไม่ได้ช่วยเว้นแต่ว่าการประมาณจะยอมรับได้ และ IP ได้รับการกล่าวถึงอยู่แล้วในคำตอบอื่น
BlueRaja - Danny Pflughoeft

@ BlueRaja-DannyPflughoeft ถูกต้อง "Despite the many crucial applications of this problem, and intense interest by researchers, no efficient algorithm is known for it.ดูที่หน้า 254 การโปรแกรมเชิงเส้นแบบจำนวนเต็มเป็นปัญหาการคำนวณที่ยากมาก ความหวังเดียวของเราที่จะมีประสิทธิภาพคือการใช้ประโยชน์จากคุณสมบัติที่แท้จริงเกี่ยวกับเมทริกซ์ของคุณเอสมันไม่ใช่สิ่งนั้นโดยพลการ
darksky

3

วิธีแก้ปัญหาโลภนี้ดูเหมือนจะถูกต้อง :

มันจะล้มเหลวในแบบ 2D แต่บางทีคุณอาจปรับปรุงได้

สำหรับ 1D:
หากมีอย่างน้อย 2 หมายเลขคุณไม่จำเป็นต้องยิงไปทางซ้ายสุดเพราะการถ่ายไปที่สองนั้นไม่ได้แย่กว่าไม่ได้เลวร้ายดังนั้นยิงไปที่สองในขณะที่ครั้งแรกไม่ใช่ 0 เพราะคุณต้องทำ ย้ายไปยังเซลล์ถัดไป อย่าลืมเซลล์สุดท้าย

รหัส C ++:

void bombs(vector<int>& v, int i, int n){
    ans += n;
    v[i] -= n;
    if(i > 0)
        v[i - 1] -= n;
    if(i + 1< v.size())
        v[i + 1] -= n;
}

void solve(vector<int> v){
    int n = v.size();
    for(int i = 0; i < n;++i){
        if(i != n - 1){
            bombs(v, i + 1, v[i]);
        }
        else
            bombs(v, i, v[i])
    }
}

ดังนั้นสำหรับ 2D:
อีกครั้ง: คุณไม่จำเป็นต้องยิงในแถวแรก (ถ้ามีครั้งที่สอง) ดังนั้นจงยิงประตูที่สอง แก้ปัญหา 1D สำหรับแถวแรก (เพราะคุณต้องทำให้ว่าง) ลงข้างล่าง. อย่าลืมแถวสุดท้าย


5
"0110","1110","1110"counterexample: คุณต้องการเพียง 1 ช็อต แต่ฉันเชื่อว่าอัลกอริทึมของคุณจะใช้ 2
maniek

2

เพื่อลดจำนวนระเบิดเราต้องเพิ่มเอฟเฟกต์ของระเบิดทุกครั้งให้มากที่สุด เพื่อให้บรรลุเป้าหมายนี้ในทุกขั้นตอนเราต้องเลือกเป้าหมายที่ดีที่สุด สำหรับแต่ละจุดรวมมันและมันเป็นเพื่อนบ้านแปด - สามารถใช้เป็นปริมาณที่มีประสิทธิภาพของการทิ้งระเบิดจุดนี้ สิ่งนี้จะช่วยให้ใกล้เคียงกับลำดับของการทิ้งระเบิดที่เหมาะสมที่สุด

UPD : เราควรคำนึงถึงจำนวนศูนย์ด้วยเนื่องจากการวางระเบิดในนั้นไม่มีประสิทธิภาพ ในความเป็นจริงปัญหาคือการลดจำนวนของศูนย์ hitted แต่เราไม่สามารถรู้ได้ว่าขั้นตอนใดที่ทำให้เราเข้าใกล้จุดมุ่งหมายนี้มากขึ้น ฉันเห็นด้วยกับความคิดที่ว่าปัญหานั้นเกิดจากปัญหา ฉันอยากได้วิธีโลภซึ่งจะให้คำตอบใกล้เคียงกับของจริง


สิ่งนี้ไม่เหมาะสม ตัวอย่างเคาน์เตอร์: 1010101, 0010100(แถวบน, แถวล่าง) วิธีการของคุณจะต้องมี 3 ซึ่งสามารถทำได้ใน 2
Mysticial

2

ฉันเชื่อว่าการลดจำนวนระเบิดคุณต้องเพิ่มความเสียหายให้มากที่สุด .. เพื่อที่จะเกิดขึ้นต้องตรวจสอบพื้นที่ที่มีกำลังแรงที่สุด .. ดังนั้นคุณต้องวิเคราะห์สนามด้วยเคอร์เนล 3x3 และตรวจสอบว่าผลรวม แข็งแกร่ง .. และวางระเบิด .. และทำจนกระทั่งทุ่งราบ .. สำหรับคำตอบนี้คือ 28

var oMatrix = [
[2,3,4,7,1],
[1,5,2,6,2],
[4,3,4,2,1],
[2,1,2,4,1],
[3,1,3,4,1],
[2,1,4,3,2],
[6,9,1,6,4]
]

var nBombs = 0;
do
{
    var bSpacesLeftToBomb = false;
    var nHigh = 0;
    var nCellX = 0;
    var nCellY = 0;
    for(var y = 1 ; y<oMatrix.length-1;y++) 
        for(var x = 1 ; x<oMatrix[y].length-1;x++)  
        {
            var nValue = 0;
            for(var yy = y-1;yy<=y+1;yy++)
                for(var xx = x-1;xx<=x+1;xx++)
                    nValue += oMatrix[yy][xx];

            if(nValue>nHigh)
            {
                nHigh = nValue;
                nCellX = x;
                nCellY = y; 
            }

        }
    if(nHigh>0)
    {
        nBombs++;

        for(var yy = nCellY-1;yy<=nCellY+1;yy++)
        {
            for(var xx = nCellX-1;xx<=nCellX+1;xx++)
            {
                if(oMatrix[yy][xx]<=0)
                    continue;
                oMatrix[yy][xx] = --oMatrix[yy][xx];
            }
        }
        bSpacesLeftToBomb = true;
    }
}
while(bSpacesLeftToBomb);

alert(nBombs+'bombs');

นี่เป็นอัลกอริธึมเดียวกับคำตอบอื่น ๆ แต่น้อยมากในภายหลัง
psr

@psr ไม่เพียงแค่นั้น มันไม่เหมาะสม
Mysticial

ฉันโพสต์มันเนื่องจากในขณะที่เสนออัลกอริทึมนี้ฉันพบว่าไม่มีการโพสต์ของรหัสหรือ "ศาสตราจารย์ของแนวคิด" ดังนั้นฉันคิดว่ามันสามารถช่วยแยกแยะ .. แต่ .. btw @ อย่างเป็นทางการคุณมีศาสตราจารย์ว่ามีวิธีที่เหมาะสมที่สุด?
CaldasGSM

@CaldasGSM ไม่ต้องกังวลปัญหาเดิม (ไม่มีการเรียงลำดับ) ยาก มีคำตอบเดียวเท่านั้นที่สามารถแก้ไขได้อย่างเหมาะสม แต่จะทำงานในเวลาที่อธิบาย
Mysticial

2

นี่คือวิธีการแก้ปัญหาที่สรุปคุณสมบัติที่ดีของมุม

สมมติว่าเราสามารถหาจุดดร็อปที่สมบูรณ์แบบสำหรับฟิลด์ที่กำหนดนั่นคือวิธีที่ดีที่สุดในการลดค่าในฟิลด์นั้น จากนั้นเพื่อค้นหาจำนวนระเบิดน้อยที่สุดที่จะทิ้งร่างแรกของอัลกอริธึมอาจเป็นได้ (รหัสนั้นถูกคัดลอกมาจากการใช้ทับทิม):

dropped_bomb_count = 0
while there_are_cells_with_non_zero_count_left
  coordinates = choose_a_perfect_drop_point
  drop_bomb(coordinates)
  dropped_bomb_count += 1
end
return dropped_bomb_count

choose_a_perfect_drop_pointความท้าทายคือ ก่อนอื่นมากำหนดว่าจุดดร็อปคืออะไร

  • จุดลดลงสำหรับการ(x, y)ลดค่าใน(x, y)ลดค่าในนอกจากนี้ยังอาจลดค่าในเซลล์อื่น ๆ
  • ลดลงจุดสำหรับเป็นที่ดีขึ้นกว่าที่เป็นจุดลดลงสำหรับถ้ามันลดค่าในเซ็ตที่เหมาะสมของเซลล์ที่ลดลง(x, y)(x, y)
  • จุดดร็อปคือสูงสุดถ้าไม่มีจุดดร็อปที่ดีกว่า
  • จุดดร็อปสองจุด(x, y)นั้นเทียบเท่ากันหากพวกมันลดชุดของเซลล์เดียวกัน
  • จุดลดลงเพื่อ(x, y)เป็นที่สมบูรณ์แบบ(x, y)ถ้ามันจะเทียบเท่ากับทุกจุดสูงสุดลดลงสำหรับ

หากมีจุดดร็อปที่สมบูรณ์แบบ(x, y)คุณจะไม่สามารถลดค่าได้(x, y)อย่างมีประสิทธิภาพมากกว่าการวางระเบิดในจุดดร็อปที่สมบูรณ์แบบสำหรับ(x, y)มีประสิทธิภาพมากขึ้นกว่าที่จะวางระเบิดบนหนึ่งในจุดที่สมบูรณ์แบบสำหรับการลดลง

จุดดร็อปที่สมบูรณ์แบบสำหรับฟิลด์ที่กำหนดคือจุดดร็อปที่สมบูรณ์แบบสำหรับเซลล์ใด ๆ ของมัน

นี่คือตัวอย่างบางส่วน:

1 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)(zero-based ดัชนี) (1, 1)คือ ทุกจุดลดลงอื่น ๆ สำหรับการ(1, 1)ที่เป็น(0, 0), (0, 1)และ(1, 0)ลดเซลล์น้อย

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

จุดลดลงที่สมบูรณ์แบบสำหรับมือถือ(2, 2)(zero-based ดัชนี) เป็น(2, 2)และยังทุกเซลล์โดยรอบ(1, 1), (1, 2), (1, 3), (2, 1), (2, 3), (3, 1), และ(3, 2)(3, 3)

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

จุดลดลงที่สมบูรณ์แบบสำหรับมือถือ(2, 2)คือ(3, 1): มันลดค่าในและความคุ้มค่าใน(2, 2) (4, 0)จุดดร็อปอื่น ๆ ทั้งหมด(2, 2)นั้นไม่ได้มากที่สุดเนื่องจากจะลดหนึ่งเซลล์น้อยลง จุดหยดน้ำที่สมบูรณ์แบบสำหรับ(2, 2)ยังเป็นจุดหยดน้ำที่สมบูรณ์แบบสำหรับ(4, 0)และเป็นจุดหยดน้ำที่สมบูรณ์แบบสำหรับสนาม มันนำไปสู่โซลูชั่นที่สมบูรณ์แบบสำหรับสาขานี้ (การทิ้งระเบิดหนึ่งครั้ง)

1 0 0 0 0
0 0 0 0 0
0 0 1 0 0
0 0 0 0 0
1 0 0 0 0

ไม่มีจุดดร็อปที่สมบูรณ์แบบสำหรับ(2, 2): ทั้งสอง(1, 1)และ(1, 3)ลดลง(2, 2)และอีกเซลล์หนึ่ง (เป็นจุดหยดสูงสุดสำหรับ(2, 2)) แต่ไม่เท่ากัน แต่(1, 1)เป็นจุดลดลงที่สมบูรณ์แบบสำหรับ(0, 0)และเป็นจุดที่สมบูรณ์แบบสำหรับการลดลง(1, 3)(0, 4)

ด้วยคำนิยามของจุดดร็อปที่สมบูรณ์แบบและลำดับของการตรวจสอบที่แน่นอนฉันได้รับผลลัพธ์ต่อไปนี้สำหรับตัวอย่างในคำถาม:

Drop bomb on 1, 1
Drop bomb on 1, 1
Drop bomb on 1, 5
Drop bomb on 1, 5
Drop bomb on 1, 5
Drop bomb on 1, 6
Drop bomb on 1, 2
Drop bomb on 1, 2
Drop bomb on 0, 6
Drop bomb on 0, 6
Drop bomb on 2, 1
Drop bomb on 2, 5
Drop bomb on 2, 5
Drop bomb on 2, 5
Drop bomb on 3, 1
Drop bomb on 3, 0
Drop bomb on 3, 0
Drop bomb on 3, 0
Drop bomb on 3, 0
Drop bomb on 3, 0
Drop bomb on 3, 4
Drop bomb on 3, 4
Drop bomb on 3, 3
Drop bomb on 3, 3
Drop bomb on 3, 6
Drop bomb on 3, 6
Drop bomb on 3, 6
Drop bomb on 4, 6
28

อย่างไรก็ตามอัลกอริทึมใช้งานได้หากมีจุดดร็อปสมบูรณ์แบบอย่างน้อยหนึ่งจุดหลังจากแต่ละขั้นตอน เป็นไปได้ที่จะสร้างตัวอย่างที่ไม่มีจุดดร็อปสมบูรณ์แบบ:

0 1 1 0
1 0 0 1
1 0 0 1
0 1 1 0

สำหรับกรณีเหล่านี้เราสามารถปรับเปลี่ยนอัลกอริทึมเพื่อให้แทนที่จะเป็นจุดดร็อปที่สมบูรณ์แบบเราเลือกพิกัดที่มีตัวเลือกน้อยที่สุดสำหรับจุดดรอปสูงสุดจากนั้นคำนวณค่าต่ำสุดสำหรับแต่ละตัวเลือก ในกรณีข้างต้นเซลล์ทั้งหมดที่มีค่าจะมีจุดดร็อปสูงสุดสองจุด ตัวอย่างเช่น(0, 1)มีจุดสูงสุดลดลงและ(1, 1) (1, 2)เลือกอย่างใดอย่างหนึ่งจากนั้นคำนวณค่าต่ำสุดที่นำไปสู่ผลลัพธ์นี้:

Drop bomb on 1, 1
Drop bomb on 2, 2
Drop bomb on 1, 2
Drop bomb on 2, 1
2

นี่เป็นอัลกอริทึมโลภที่แสดงไว้ด้านบน
darksky

มันเป็นอัลกอริทึมแบบโลภเช่นกัน แต่แทนที่จะเน้นที่มุมและขอบฉันกำหนดวิธีเลือกจุดดร็อปถัดไป ด้วยตารางตัวอย่างของ 5x7 ทำให้ง่ายต่อการพูดคุยเกี่ยวกับมุมในฟิลด์ 1000x1000 ไม่มากนัก หากคุณตรวจสอบลำดับที่อัลกอริทึมของฉันล้างฟิลด์นั้นไม่ใช่จากภายนอก แต่เป็นจากบนลงล่าง / ซ้ายไปขวา
Tammo Freese

2

นี่คือแนวคิดอื่น:

เริ่มต้นด้วยการกำหนดน้ำหนักให้แต่ละช่องว่างบนกระดานเพื่อลดจำนวนตัวเลขโดยการวางระเบิดที่นั่น ดังนั้นหากพื้นที่มีจำนวนที่ไม่เป็นศูนย์จะได้รับจุดและหากพื้นที่ใด ๆ ที่อยู่ติดกับมันมีจำนวนที่ไม่เป็นศูนย์ก็จะได้รับจุดเพิ่มเติม ดังนั้นถ้ามีกริด 1,000 คูณ 1,000 เรามีน้ำหนักที่กำหนดให้กับแต่ละพื้นที่ 1 ล้าน

จากนั้นเรียงลำดับรายการของช่องว่างตามน้ำหนักแล้ววางระเบิดที่มีน้ำหนักสูงสุด นี่คือสิ่งที่ได้รับมากที่สุดสำหรับเจ้าชู้ของเราดังนั้นที่จะพูด

หลังจากนั้นอัปเดตน้ำหนักของทุกพื้นที่ที่มีน้ำหนักได้รับผลกระทบจากระเบิด นี่จะเป็นพื้นที่ที่คุณทิ้งระเบิดและพื้นที่ใด ๆ ที่อยู่ติดกับมันทันทีและพื้นที่ใด ๆ ที่อยู่ติดกับพื้นที่เหล่านั้นทันที กล่าวอีกนัยหนึ่งช่องว่างใด ๆ ที่อาจมีมูลค่าลดลงเป็นศูนย์โดยการทิ้งระเบิดหรือมูลค่าของพื้นที่ใกล้เคียงลดลงเป็นศูนย์

จากนั้นจัดเรียงพื้นที่รายการใหม่ตามน้ำหนัก เนื่องจากช่องว่างเล็ก ๆ น้อย ๆ เท่านั้นที่มีการเปลี่ยนแปลงน้ำหนักโดยการทิ้งระเบิดคุณไม่จำเป็นต้องหันมาใช้รายชื่อทั้งหมดเพียงแค่ย้ายสิ่งเหล่านั้นไปรอบ ๆ ในรายการ

ระเบิดพื้นที่ว่างน้ำหนักสูงสุดใหม่และทำซ้ำขั้นตอน

สิ่งนี้รับประกันได้ว่าการทิ้งระเบิดทุกครั้งจะช่วยลดช่องว่างให้มากที่สุดเท่าที่จะเป็นไปได้ (โดยทั่วไปแล้วมันจะกระทบกับพื้นที่ไม่กี่แห่งที่เป็นศูนย์อยู่แล้ว) ดังนั้นมันจะดีที่สุด ดังนั้นคุณอาจต้องทำการติดตามกลับเมื่อมีการเสมอกันสำหรับน้ำหนักสูงสุด เฉพาะเน็คไทสำหรับเรื่องที่มีน้ำหนักสูงสุดเท่านั้นไม่ใช่ความสัมพันธ์แบบอื่นดังนั้นหวังว่ามันจะไม่ติดตามย้อนหลังมากเกินไป

แก้ไข: ตัวอย่างด้านล่างของ Mysticial แสดงให้เห็นว่าที่จริงแล้วสิ่งนี้ไม่ได้รับประกันว่าจะดีที่สุดโดยไม่คำนึงถึงน้ำหนัก ในบางกรณีการลดน้ำหนักให้มากที่สุดเท่าที่จะเป็นไปได้ในขั้นตอนที่กำหนดจริง ๆ แล้วปล่อยให้ลูกระเบิดที่เหลือกระจายออกไปมากเกินไปเพื่อให้ได้การลดการ cummulative สูงหลังจากขั้นตอนที่สองเท่าที่คุณจะทำได้ ฉันค่อนข้างเข้าใจผิดโดยความคิดที่ว่าผลลัพธ์ไม่ตอบสนองต่อการวางระเบิด พวกเขาคือไม่ตอบสนองต่อการสั่งซื้อที่คุณสามารถใช้ระเบิดได้หลายชุดและเล่นซ้ำตั้งแต่เริ่มต้นในลำดับที่แตกต่างกันและจบลงด้วยกระดานผลลัพธ์เดียวกัน แต่มันไม่ได้เป็นไปตามนั้นคุณสามารถพิจารณาการวางระเบิดแต่ละครั้งอย่างอิสระ หรืออย่างน้อยการทิ้งระเบิดแต่ละครั้งจะต้องพิจารณาด้วยวิธีการที่คำนึงถึงว่ามันตั้งคณะกรรมการสำหรับการวางระเบิดที่ตามมาได้ดีเพียงใด


มันจะยังคงมีการย้อนรอยจำนวนมากในตอนแรกเนื่องจากทุ่งนามีศูนย์น้อยมากน้ำหนักของเซลล์ส่วนใหญ่จะเป็นเก้าทั้งหมด
Lie Ryan

ใช่นั่นเป็นจุดที่ดีเนื่องจากมีช่วงน้ำหนักไม่มากนัก (เพียง 0 ถึง 9)
ทิมกู๊ดแมน

ฉันยังไม่แน่ใจ 100% ว่าความจำเป็นในการ backtracking คืออะไร ... มันอาจเป็นคำแนะนำในการสร้างกริดซึ่งการเลือกวางระเบิดโลภหนึ่งตัวเลือกด้อยกว่าการวางระเบิดโลภอีกตัวหนึ่ง อาจมีวิธีที่สอดคล้องกันในการคาดการณ์ว่าจะดีกว่า
ทิมกู๊ดแมน

อันที่จริงฉันเห็นพันเอก Panic ได้ทำสิ่งนี้ในคำตอบของเขา เหตุผลหนึ่งที่ทางเลือกที่โลภสามารถทำได้ดีกว่าตัวเลือกอื่นคือเหตุผลหนึ่งที่ทำให้จำนวนที่เหลือกระจายออกไปมากขึ้น
ทิมกู๊ดแมน

3
1010101, 0010100อาจจะ counterexample ที่พิสูจน์วิธีการนี้จะไม่เหมาะสม วิธีนี้ต้องใช้ 3. สามารถทำได้ใน 2
Mysticial

1

สมมติว่าเรานับจำนวนตำแหน่งของบอร์ด 1, 2, ... , nx m ลำดับของการวางระเบิดใด ๆ สามารถแทนได้ด้วยลำดับของตัวเลขในชุดนี้ซึ่งตัวเลขสามารถทำซ้ำได้ อย่างไรก็ตามเอฟเฟกต์บนกระดานจะเหมือนกันไม่ว่าคุณจะวางระเบิดในลำดับใดดังนั้นตัวเลือกการวางระเบิดจะสามารถแสดงเป็นรายการของตัวเลข nxm โดยที่หมายเลขแรกแสดงถึงจำนวนของการทิ้งระเบิดที่ตำแหน่ง 1 ตัวเลขที่สองแสดงถึงจำนวนของระเบิดที่ทิ้งไว้ที่ตำแหน่ง 2 เป็นต้นมาเรียกรายการนี้ว่า nxm numbers "key"

คุณสามารถลองคำนวณสถานะกระดานทั้งหมดที่เกิดจากการทิ้งระเบิด 1 ครั้งจากนั้นใช้สิ่งเหล่านี้เพื่อคำนวณสถานะกระดานทั้งหมดที่เกิดจากการทิ้งระเบิด 2 ครั้งเป็นต้นจนกว่าคุณจะได้ศูนย์ทั้งหมด แต่ในแต่ละขั้นตอนคุณจะแคชสถานะโดยใช้คีย์ที่ฉันกำหนดไว้ด้านบนดังนั้นคุณสามารถใช้ผลลัพธ์เหล่านี้ในการคำนวณขั้นตอนต่อไป (วิธีการ "โปรแกรมแบบไดนามิก")

แต่ขึ้นอยู่กับขนาดของ n, m และตัวเลขในตารางความต้องการหน่วยความจำของวิธีนี้อาจมากเกินไป คุณสามารถทิ้งผลลัพธ์ทั้งหมดสำหรับระเบิด N ทิ้งได้เมื่อคุณคำนวณผลลัพธ์ทั้งหมดสำหรับ N + 1 ดังนั้นจึงประหยัดได้ และแน่นอนว่าคุณไม่สามารถแคชอะไรก็ได้โดยใช้เวลานานกว่านั้นมาก - วิธีการเขียนโปรแกรมแบบไดนามิกทำการซื้อขายหน่วยความจำด้วยความเร็ว


1
สงสัยว่าเป็นไปได้ตั้งแต่ (ถ้าฉันเข้าใจคุณถูกต้อง) n = m ฉันต้องการพอยน์ int 10 ^ 6 ถึง (10 ^ 6) ^ 2 เซลล์ int ฉันมีบอร์ดมากพอ ๆ กับกุญแจในโต๊ะ 10 ^ 12 สงสัยฉันสามารถจัดสรรมากในเครื่อง 32 บิต
abc

ใช่ฉันเพิ่งเห็นความคิดเห็นของคุณเกี่ยวกับกระดานที่มีมากถึง 1,000 ต่อ 1,000 ดังนั้นนั่นเป็นล้าน ints สำหรับสถานะของแต่ละกระดานบวกหนึ่งล้าน ints สำหรับจำนวนระเบิดวางในแต่ละตำแหน่ง ดังนั้นสำหรับคณะกรรมการที่คุณเก็บแต่ละท่านต้อง 2 ล้าน ints และมีจำนวนมากของบอร์ดเป็นไปได้ ...
ทิมสามี

ฉันได้เพิ่มคำตอบที่สองซึ่งใช้วิธีการที่แตกต่างกัน
ทิมกู๊ดแมน

1
ใช่. เป็นวิธีการที่ดุร้าย แต่ฉันคิดว่ามันไม่ค่อยมีประโยชน์สำหรับกระดานขนาดใหญ่
ทิมกู๊ดแมน

@ Kostek ทำไมประมาณต่ำ มันเป็นเหมือนหน่วยความจำ k ^ (m * n) โดยที่ k เป็นข้อ จำกัด สำหรับตัวเลขที่บอร์ดเต็มไปด้วย
Rotsor

1

หากคุณต้องการทางออกที่ดีที่สุดแน่นอนในการทำความสะอาดบอร์ดคุณจะต้องใช้ backtracking แบบคลาสสิก แต่ถ้าเมทริกซ์มีขนาดใหญ่มากมันจะใช้เวลานานในการหาทางออกที่ดีที่สุดถ้าคุณต้องการทางออกที่ดีที่สุด "เป็นไปได้" คุณสามารถใช้อัลกอริทึมโลภ ถ้าคุณต้องการความช่วยเหลือในการเขียนอัลกอริทึมฉันสามารถช่วยคุณ

ลองคิดดูสิว่าเป็นวิธีที่ดีที่สุด ทำเมทริกซ์อื่นที่นั่นคุณเก็บคะแนนที่คุณลบโดยการวางระเบิดที่นั่นจากนั้นเลือกเซลล์ที่มีคะแนนสูงสุดและวางระเบิดที่นั่นอัปเดตเมทริกซ์คะแนนและดำเนินการต่อ ตัวอย่าง:

2 3 5 -> (2+(1*3)) (3+(1*5)) (5+(1*3))
1 3 2 -> (1+(1*4)) (3+(1*7)) (2+(1*4))
1 0 2 -> (1+(1*2)) (0+(1*5)) (2+(1*2))

ค่าของเซลล์ +1 สำหรับทุกเซลล์ที่อยู่ติดกันที่มีค่าสูงกว่า 0


7
จะต้องใช้ backtracking คุณมีหลักฐานสำหรับสิ่งนี้หรือไม่?
Shahbaz

ฉันไม่แน่ใจ. มันมาจากการประกวดที่ฉันเตรียมไว้ ขีด จำกัด คือ 1 <= n, m <= 1,000 (ไม่ทราบว่าใหญ่หรือไม่) อย่างไรก็ตามคุณต้องการคำตอบที่แน่นอน (คล้ายกับการประกวด CERC และอื่น ๆ ) ไม่มีการ จำกัด เวลาดังนั้นไม่มีคำตอบไม่มีคำตอบในหน้าการแข่งขัน
abc

อัลกอริทึมอื่น ๆ จะให้ทางออกที่ดีที่สุดแก่คุณ แต่จนกว่าคุณจะลองทั้งหมด (backtracking) คุณจะไม่รู้ว่าโซลูชันนั้นดีที่สุดหรือไม่
cosmin.danisor

2
คุณไม่จำเป็นต้องใช้การย้อนรอยเนื่องจากมันเป็นการผสมผสานที่คุณค้นหาไม่ใช่การเปลี่ยนรูป ลำดับของการทิ้งระเบิดไม่สำคัญ
Luka Rahne

จากนั้นคุณสามารถลองใช้รูปแบบของความโลภ ในทุกขั้นตอนสร้างเมทริกซ์ใหม่และทุกจุดจะมีค่าของเซลล์ของเขา + 1 สำหรับทุกเซลล์ถัดจากมัน> 0 วิธีนี้จะเลือกที่ดีกว่าที่จะวางระเบิดถัดไป
cosmin.danisor

1

กำลังดุร้าย !

ฉันรู้ว่ามันไม่ได้มีประสิทธิภาพ แต่แม้ว่าคุณจะพบอัลกอริทึมที่เร็วกว่าคุณสามารถทดสอบกับผลลัพธ์นี้เพื่อให้รู้ว่ามันแม่นยำแค่ไหน

ใช้การสอบถามซ้ำเช่นนี้:

void fn(tableState ts, currentlevel cl)
{
  // first check if ts is all zeros yet, if not:
  //
  // do a for loop to go through all cells of ts, 
  // for each cell do a bomb, and then
  // call: 
  // fn(ts, cl + 1);

}

คุณสามารถทำให้มีประสิทธิภาพมากขึ้นโดยการแคชหากวิธีการอื่นนำไปสู่ผลลัพธ์เดียวกันคุณไม่ควรทำซ้ำขั้นตอนเดียวกัน

ทำอย่างละเอียด:

หากการทิ้งระเบิด 1,3,5 นำไปสู่ผลลัพธ์เช่นเดียวกับการทิ้งระเบิด 5,3,1 ดังนั้นคุณไม่ควรทำขั้นตอนต่อไปอีกครั้งสำหรับทั้งสองกรณีเพียง 1 ก็เพียงพอแล้วคุณควรเก็บไว้ที่อื่นทั้งหมด ระบุตารางและใช้ผลลัพธ์

สามารถใช้แฮชของสถิติตารางเพื่อทำการเปรียบเทียบอย่างรวดเร็ว


1
  1. ห้ามวางระเบิดในเขต (เว้นแต่จัตุรัสจะไม่มีเพื่อนบ้านที่ไม่มีพรมแดน)
  2. มุมศูนย์
  3. หากต้องการมุมศูนย์ให้วางค่าของมุมหนึ่งตารางออกเป็นแนวเส้นทแยงมุม
  4. สิ่งนี้จะสร้างมุมใหม่ ไปที่ 2

แก้ไข: ไม่ได้สังเกตว่า Kostek แนะนำวิธีการเกือบเหมือนกันดังนั้นตอนนี้ฉันได้รับการเรียกร้องที่แข็งแกร่ง: หากเลือกมุมที่ชัดเจนเพื่อให้อยู่ในชั้นนอกสุดเสมอ

ในตัวอย่างของ OP: การดรอป 2 (เป็น 1 + 1 หรือ 2) ในสิ่งอื่นที่ไม่ใช่ 5 จะไม่นำไปสู่การชนสี่เหลี่ยมจัตุรัสใด ๆ ดังนั้นเราต้องเลื่อน 2 บน 5 (และ 6 ที่ซ้ายล่าง 1 ... )

หลังจากนี้มีเพียงวิธีเดียวที่จะล้างมุม (ซ้ายบน) สิ่งที่เป็นต้นฉบับ 1 (ตอนนี้ 0) และนั่นคือโดยการลดลง 0 ใน B3 (excel เช่นสัญกรณ์) และอื่น ๆ

หลังจากล้างคอลัมน์ A และ E ทั้งหมดและ 1 และ 7 แถวเท่านั้นให้เริ่มล้างชั้นหนึ่งลึก

ลองพิจารณาเฉพาะการลบทิ้งโดยเจตนาเท่านั้นการล้างมุมมูลค่า 0 ไม่มีค่าใช้จ่ายและทำให้การคิดง่ายขึ้น

เนื่องจากทิ้งระเบิดทั้งหมดด้วยวิธีนี้จะต้องลดลงและสิ่งนี้นำไปสู่ทุ่งโล่งมันเป็นทางออกที่ดีที่สุด


หลังจากนอนหลับสนิทฉันก็รู้ว่านี่ไม่เป็นความจริง พิจารณา

  ABCDE    
1 01000
2 10000
3 00000
4 00000

วิธีการของฉันจะทิ้งระเบิดที่ B3 และ C2 เมื่อวางบน B2 จะเพียงพอ


แต่นี่เป็นสิ่งที่ดีที่สุดใช่ไหม
Mysticial

7
มุมใหม่สามารถวางระเบิดได้ 2 วิธี (ถ้าจุดมุมส่วนใหญ่มีค่าต่ำสุดทั้ง 4 ค่า) ระเบิดชนิดใดที่เหมาะสมที่สุด?
abc

ฉันกำลังคิดเกี่ยวกับวิธีการที่คล้ายกันและเมื่อคุณถึงสถานการณ์เช่น Kostek อธิบายแล้วเริ่มใช้ backtracking ...
Karoly Horvath

มุมทำให้คุณมีจำนวนระเบิดน้อยที่สุดที่จะทิ้งบนพื้นที่ทแยงมุม แต่เมื่อคุณ zeroed พวกเขากระเบื้องชายแดนถัดไปจะไม่จำเป็นต้องมีจุดที่เหมาะสมที่ชัดเจน มันเป็นวิธีที่ดีในการลดพื้นที่การค้นหา
Eugene

สิ่งที่เกี่ยวกับการเลือกเส้นทแยงมุมมุมใหม่ที่ให้ผลรวมการนับสูงสุดในกล่องตี?
ผู้พิพากษา Maygarden

1

นี่คือวิธีการแก้ปัญหาของฉัน .. ฉันจะไม่เขียนมันในโค้ดเนื่องจากฉันไม่มีเวลา แต่ฉันเชื่อว่าสิ่งนี้ควรสร้างจำนวนการเคลื่อนไหวที่ดีที่สุดในแต่ละครั้ง - แม้ว่าฉันจะไม่แน่ใจ คะแนนที่จะระเบิด

อันดับแรกตามที่ @Luka Rahne ระบุไว้ในความคิดเห็นหนึ่งคำสั่งที่คุณวางระเบิดไม่ได้มีความสำคัญ - เป็นการรวมกันเท่านั้น

ประการที่สองตามที่คนอื่น ๆ ได้กล่าวไว้การทิ้งระเบิด 1-off ในแนวทแยงจากมุมนั้นเหมาะสมที่สุดเพราะมันแตะได้มากกว่าคะแนน

สิ่งนี้สร้างพื้นฐานสำหรับอัลกอริทึมเวอร์ชั่นของฉัน: เราสามารถวางระเบิด '1-off จากมุม' แรกหรือสุดท้ายมันไม่สำคัญ (ในทางทฤษฎี) เราวางระเบิดสิ่งเหล่านั้นก่อนเพราะมันทำให้การตัดสินใจในภายหลังง่ายขึ้น (ในทางปฏิบัติ) เราวางระเบิดจุดที่มีผลต่อคะแนนมากที่สุดในขณะเดียวกันก็วางระเบิดมุมเหล่านั้น

ลองกำหนดคะแนนความต้านทานเป็นคะแนนในกระดานด้วยคะแนนที่ไม่สามารถระเบิดได้มากที่สุด + จำนวน 0 ที่มากที่สุดรอบตัวพวกเขา

คะแนนที่ไม่สามารถระเบิดได้สามารถกำหนดเป็นคะแนนที่ไม่มีอยู่ในขอบเขตปัจจุบันของคณะกรรมการที่เรากำลังดูอยู่

ฉันจะกำหนดขอบเขตที่ 4 ซึ่งจะจัดการขอบเขตของเรา: Top = 0, Left = 0, Bottom = k, right = j (ค่าเริ่มต้น)

ในที่สุดฉันจะกำหนดระเบิดที่ดีที่สุดเป็นระเบิดที่วางอยู่บนจุดที่อยู่ติดกับจุดต่อต้านและสัมผัส (1) จุดต้านทานที่มีค่าสูงสุดและ (2) จุดที่มีจำนวนมากที่สุด

เกี่ยวกับวิธีการ - มันชัดเจนว่าเรากำลังทำงานจากภายนอกเราจะสามารถทำงานร่วมกับ 'เครื่องบินทิ้งระเบิด' ในเวลาเดียวกัน

จุดต้านทานแรกคือมุมของเรา คะแนน 'ไม่อยู่ที่ขอบเขต' จะไม่สามารถวางระเบิดได้ (มี 5 คะแนนนอกขอบเขตสำหรับแต่ละมุม) ดังนั้นเราจึงวางระเบิดจุดตามแนวทแยงมุมมุมหนึ่งก่อน

ขั้นตอนวิธีการ:

  1. ค้นหาจุดระเบิดที่เหมาะสมที่สุด 4 จุด
  2. หากจุดระเบิดวางระเบิดจุดต้านทานที่สัมผัส 2 ขอบเขต (เช่นมุม) ให้ระเบิดจนจุดนั้นเป็น 0 มิฉะนั้นให้ระเบิดแต่ละจุดจนกระทั่งจุดต้านทานจุดหนึ่งสัมผัสกับจุดระเบิดที่เหมาะสมที่สุดคือ 0
  3. สำหรับแต่ละขอบเขต: ถ้า (ผลรวม (ผูก) == 0) ผูกไว้ล่วงหน้า

ทำซ้ำจนกระทั่งด้านบน = ด้านล่างและด้านซ้าย = ขวา

ฉันจะพยายามเขียนรหัสจริงในภายหลัง


1

คุณสามารถใช้การวางแผนพื้นที่ของรัฐ ตัวอย่างเช่นการใช้ A * (หรือหนึ่งในสายพันธุ์ของมัน) ควบคู่กับฮิวริสติกf = g + hเช่นนี้:

  • g: จำนวนระเบิดลดลงเรื่อย ๆ
  • h: รวมค่าทั้งหมดของกริดหารด้วย 9 (ซึ่งเป็นผลลัพธ์ที่ดีที่สุดหมายถึงเรามีฮิวริสติกที่ยอมรับได้)

1

ฉันได้รับการเคลื่อนไหว 28 ครั้งเช่นกัน ฉันใช้การทดสอบสองครั้งเพื่อการก้าวต่อไปที่ดีที่สุดก่อนอื่นการเคลื่อนที่จะสร้างผลรวมขั้นต่ำสำหรับกระดาน ประการที่สองสำหรับผลรวมเท่ากันการย้ายทำให้เกิดความหนาแน่นสูงสุดซึ่งกำหนดเป็น:

number-of-zeros / number-of-groups-of-zeros

นี่คือ Haskell "แก้ปัญหาบอร์ด" แสดงวิธีแก้ปัญหาของเครื่องยนต์ คุณสามารถเล่นเกมโดยพิมพ์ "main" จากนั้นป้อนจุดเป้าหมาย "ดีที่สุด" สำหรับคำแนะนำหรือ "เลิก" เพื่อออก

เอาท์พุท:
* หลัก> แก้บอร์ด
[(4,4), (3,6), (3,3), (2,2), (2,2), (2,2), (4,6), (4,6), (2,6), (3,2), (4,2), (2,6), (3,3), (4,3), (2,6), (4,2), (4 6), (4,6), (3,6), (2,6), (2,6), (2,4), (2,4), (2,6), (3,6 ), (4,2), (4,2), (4,2), (4,2)]

import Data.List
import Data.List.Split
import Data.Ord
import Data.Function(on)

board = [2,3,4,7,1,
         1,5,2,6,2,
         4,3,4,2,1,
         2,1,2,4,1,
         3,1,3,4,1,
         2,1,4,3,2,
         6,9,1,6,4]

n = 5
m = 7

updateBoard board pt =
  let x = fst pt
      y = snd pt
      precedingLines = replicate ((y-2) * n) 0
      bomb = concat $ replicate (if y == 1
                                    then 2
                                    else min 3 (m+2-y)) (replicate (x-2) 0 
                                                         ++ (if x == 1 
                                                                then [1,1]
                                                                else replicate (min 3 (n+2-x)) 1)
                                                                ++ replicate (n-(x+1)) 0)
  in zipWith (\a b -> max 0 (a-b)) board (precedingLines ++ bomb ++ repeat 0)

showBoard board = 
  let top = "   " ++ (concat $ map (\x -> show x ++ ".") [1..n]) ++ "\n"
      chunks = chunksOf n board
  in putStrLn (top ++ showBoard' chunks "" 1)
       where showBoard' []     str count = str
             showBoard' (x:xs) str count =
               showBoard' xs (str ++ show count ++ "." ++ show x ++ "\n") (count+1)

instances _ [] = 0
instances x (y:ys)
  | x == y    = 1 + instances x ys
  | otherwise = instances x ys

density a = 
  let numZeros = instances 0 a
      groupsOfZeros = filter (\x -> head x == 0) (group a)
  in if null groupsOfZeros then 0 else numZeros / fromIntegral (length groupsOfZeros)

boardDensity board = sum (map density (chunksOf n board))

moves = [(a,b) | a <- [2..n-1], b <- [2..m-1]]               

bestMove board = 
  let lowestSumMoves = take 1 $ groupBy ((==) `on` snd) 
                              $ sortBy (comparing snd) (map (\x -> (x, sum $ updateBoard board x)) (moves))
  in if null lowestSumMoves
        then (0,0)
        else let lowestSumMoves' = map (\x -> fst x) (head lowestSumMoves) 
             in fst $ head $ reverse $ sortBy (comparing snd) 
                (map (\x -> (x, boardDensity $ updateBoard board x)) (lowestSumMoves'))   

solve board = solve' board [] where
  solve' board result
    | sum board == 0 = result
    | otherwise      = 
        let best = bestMove board 
        in solve' (updateBoard board best) (result ++ [best])

main :: IO ()
main = mainLoop board where
  mainLoop board = do 
    putStrLn ""
    showBoard board
    putStr "Pt: "
    a <- getLine
    case a of 
      "quit"    -> do putStrLn ""
                      return ()
      "best"    -> do putStrLn (show $ bestMove board)
                      mainLoop board
      otherwise -> let ws = splitOn "," a
                       pt = (read (head ws), read (last ws))
                   in do mainLoop (updateBoard board pt)

1

ดูเหมือนว่าจะมีโครงสร้างย่อยการจับคู่แบบ nonbipartite ที่นี่ พิจารณาตัวอย่างต่อไปนี้:

0010000
1000100
0000001
1000000
0000001
1000100
0010000

ทางออกที่ดีที่สุดสำหรับเคสนี้คือขนาด 5 เนื่องจากขนาดของฝาปิดขั้นต่ำของจุดยอดของรอบ 9 โดยขอบ

โดยเฉพาะอย่างยิ่งกรณีนี้แสดงให้เห็นว่าการเขียนโปรแกรมเชิงเส้นผ่อนคลายบางคนโพสต์ไม่ถูกต้องไม่ทำงานและสิ่งเลวร้ายอื่น ๆ ทั้งหมด ฉันค่อนข้างมั่นใจว่าฉันสามารถลด "ครอบคลุมจุดยอดของกราฟลูกบาศก์มุมระนาบของฉันให้น้อยที่สุดเท่าที่จะทำได้" กับปัญหาของคุณซึ่งทำให้ฉันสงสัยว่าโซลูชันโลภ / ปีนเขากำลังทำงานอยู่หรือไม่

ฉันไม่เห็นวิธีที่จะแก้ปัญหานี้ในเวลาพหุนามในกรณีที่เลวร้ายที่สุด อาจมีวิธีแก้ปัญหาไบนารี - การค้นหาและ DP ที่ชาญฉลาดที่ฉันไม่เห็น

แก้ไข : ฉันเห็นว่าการแข่งขัน ( http://deadline24.pl ) เป็นผู้ไม่เชื่อเรื่องภาษา พวกมันจะส่งไฟล์อินพุตให้คุณเป็นจำนวนมากและคุณส่งผลลัพธ์ออกมา ดังนั้นคุณไม่ต้องการสิ่งที่ทำงานในเวลาพหุนามกรณีที่เลวร้ายที่สุด โดยเฉพาะอย่างยิ่งคุณจะได้เห็นอินพุต !

มีหลายกรณีเล็ก ๆ ในอินพุต จากนั้นมีเคส 10x1000 ตัวเคส 100x100 และเคส 1000x1000 กรณีขนาดใหญ่ทั้งสามคดีล้วนมีความประพฤติดีมาก โดยทั่วไปรายการที่อยู่ติดกันในแนวนอนจะมีค่าเหมือนกัน บนเครื่องที่ค่อนข้างอ้วนฉันสามารถแก้ปัญหาทั้งหมดโดยการบังคับให้ใช้ CPLEX ในเวลาเพียงไม่กี่นาที ฉันโชคดีที่ 1,000x1000 การผ่อนคลาย LP เกิดขึ้นเพื่อให้ได้ทางออกที่ดีที่สุด โซลูชันของฉันเห็นด้วยกับ.ansไฟล์ที่ให้ไว้ในชุดข้อมูลทดสอบ

ฉันพนันได้เลยว่าคุณสามารถใช้โครงสร้างของอินพุตในลักษณะที่ตรงกว่าที่ฉันเคยทำหากคุณลองดู ดูเหมือนว่าคุณจะสามารถตัดแถวแรกหรือสองหรือสามอย่างซ้ำ ๆ จนกว่าคุณจะไม่เหลืออะไรเลย (ดูเหมือนว่าใน 1,000x1000 แถวทั้งหมดจะไม่เพิ่มขึ้นหรือไม่ฉันคิดว่านั่นคือ "ส่วน B" ของคุณมาจากไหน?)


ได้. บางครั้งฉันก็ข้ามส่วน "ไม่เกี่ยวข้อง" ของข้อความ เพิ่งได้รับความคิดสั้น ๆ และอื่น ๆ คราวนี้มันเปลี่ยนระดับจากง่ายเป็นยากเหมือนเดิม: P อย่างไรก็ตามฉันรู้ว่าคุณสามารถลองใช้ฮิวริสติกที่มีชุดอินพุต "ที่รู้จัก" ในทางกลับกันฉันแค่คิดว่าถ้าคำตอบไม่ใช่คะแนนร้อยละจะต้องมีอัลกอริธึมที่จะทำงานได้อย่างง่ายดายในช่วง 5 ชั่วโมง สิ่งที่ฉันพบมีความซับซ้อนมากเกินไป จากนั้นฉันก็อ่านมันอย่างระมัดระวังมากขึ้นเมื่อใครสักคนถามเกี่ยวกับที่มา :)
abc

เราสามารถพูดได้ว่าต้องขอบคุณหลายคนที่มีปัญหาที่ดีคิด แต่สงสัยว่ามันสามารถทำได้ในเวลาพหุนาม มันตลกว่าข้อ จำกัด ง่ายๆเพียงใดเปลี่ยนระดับของงานจากง่ายไปไม่ได้
abc

@Kostek: ขออภัยถ้าฉันไม่ชัดเจน ฉัน ... คำอธิบายที่ไม่เหมาะสมในระดับที่เหมาะสมสำหรับผู้ชม :) ฉันไม่ทราบว่าอยู่ที่ไหน
tmyklebu

1

ฉันไม่สามารถคิดวิธีการคำนวณจำนวนจริงโดยไม่ต้องคำนวณแคมเปญระเบิดโดยใช้ฮิวริสติกที่ดีที่สุดของฉันและหวังว่าฉันจะได้รับผลลัพธ์ที่สมเหตุสมผล

ดังนั้นวิธีการของฉันคือการคำนวณตัวชี้วัดประสิทธิภาพการทิ้งระเบิดสำหรับแต่ละเซลล์ระเบิดเซลล์ด้วยค่าสูงสุด .... ทำซ้ำกระบวนการจนกว่าฉันจะทำทุกอย่างให้แบน บางคนสนับสนุนโดยใช้ความเสียหายที่อาจเกิดขึ้นง่าย ๆ (เช่นคะแนนจาก 0 ถึง 9) เป็นตัวชี้วัด แต่มันสั้นลงโดยการทุบเซลล์ที่มีมูลค่าสูงและไม่ใช้ความเสียหายที่ทับซ้อนกัน ฉันคำนวณคำนวณcell value - sum of all neighbouring cellsรีเซ็ตค่าเป็นบวกเป็น 0 และใช้ค่าสัมบูรณ์ของค่าลบใด ๆ ตัวชี้วัดนี้โดยสังหรณ์ใจควรเลือกซึ่งจะช่วยเพิ่มความเสียหายที่ทับซ้อนกันบนเซลล์ที่มีค่าสูงแทนการทุบโดยตรง

รหัสด้านล่างนี้ไปถึงการทำลายทั้งหมดของสนามทดสอบใน 28 ลูกระเบิด (โปรดทราบว่าการใช้ความเสียหายที่อาจเกิดขึ้นเมื่อเมตริกเป็นอัตราผลตอบแทน 31!)

using System;
using System.Collections.Generic;
using System.Linq;

namespace StackOverflow
{
  internal class Program
  {
    // store the battle field as flat array + dimensions
    private static int _width = 5;
    private static int _length = 7;
    private static int[] _field = new int[] {
        2, 3, 4, 7, 1,
        1, 5, 2, 6, 2,
        4, 3, 4, 2, 1,
        2, 1, 2, 4, 1,
        3, 1, 3, 4, 1,
        2, 1, 4, 3, 2,
        6, 9, 1, 6, 4
    };
    // this will store the devastation metric
    private static int[] _metric;

    // do the work
    private static void Main(string[] args)
    {
        int count = 0;

        while (_field.Sum() > 0)
        {
            Console.Out.WriteLine("Round {0}:", ++count);
            GetBlastPotential();
            int cell_to_bomb = FindBestBombingSite();
            PrintField(cell_to_bomb);
            Bomb(cell_to_bomb);
        }
        Console.Out.WriteLine("Done in {0} rounds", count);
    } 

    // convert 2D position to 1D index
    private static int Get1DCoord(int x, int y)
    {
        if ((x < 0) || (y < 0) || (x >= _width) || (y >= _length)) return -1;
        else
        {
            return (y * _width) + x;
        }
    }

    // Convert 1D index to 2D position
    private static void Get2DCoord(int n, out int x, out int y)
    {
        if ((n < 0) || (n >= _field.Length))
        {
            x = -1;
            y = -1;
        }
        else
        {
            x = n % _width;
            y = n / _width;
        }
    }

    // Compute a list of 1D indices for a cell neighbours
    private static List<int> GetNeighbours(int cell)
    {
        List<int> neighbours = new List<int>();
        int x, y;
        Get2DCoord(cell, out x, out y);
        if ((x >= 0) && (y >= 0))
        {
            List<int> tmp = new List<int>();
            tmp.Add(Get1DCoord(x - 1, y - 1));
            tmp.Add(Get1DCoord(x - 1, y));
            tmp.Add(Get1DCoord(x - 1, y + 1));
            tmp.Add(Get1DCoord(x, y - 1));
            tmp.Add(Get1DCoord(x, y + 1));
            tmp.Add(Get1DCoord(x + 1, y - 1));
            tmp.Add(Get1DCoord(x + 1, y));
            tmp.Add(Get1DCoord(x + 1, y + 1));

            // eliminate invalid coords - i.e. stuff past the edges
            foreach (int c in tmp) if (c >= 0) neighbours.Add(c);
        }
        return neighbours;
    }

    // Compute the devastation metric for each cell
    // Represent the Value of the cell minus the sum of all its neighbours
    private static void GetBlastPotential()
    {
        _metric = new int[_field.Length];
        for (int i = 0; i < _field.Length; i++)
        {
            _metric[i] = _field[i];
            List<int> neighbours = GetNeighbours(i);
            if (neighbours != null)
            {
                foreach (int j in neighbours) _metric[i] -= _field[j];
            }
        }
        for (int i = 0; i < _metric.Length; i++)
        {
            _metric[i] = (_metric[i] < 0) ? Math.Abs(_metric[i]) : 0;
        }
    }

    //// Compute the simple expected damage a bomb would score
    //private static void GetBlastPotential()
    //{
    //    _metric = new int[_field.Length];
    //    for (int i = 0; i < _field.Length; i++)
    //    {
    //        _metric[i] = (_field[i] > 0) ? 1 : 0;
    //        List<int> neighbours = GetNeighbours(i);
    //        if (neighbours != null)
    //        {
    //            foreach (int j in neighbours) _metric[i] += (_field[j] > 0) ? 1 : 0;
    //        }
    //    }            
    //}

    // Update the battle field upon dropping a bomb
    private static void Bomb(int cell)
    {
        List<int> neighbours = GetNeighbours(cell);
        foreach (int i in neighbours)
        {
            if (_field[i] > 0) _field[i]--;
        }
    }

    // Find the best bombing site - just return index of local maxima
    private static int FindBestBombingSite()
    {
        int max_idx = 0;
        int max_val = int.MinValue;
        for (int i = 0; i < _metric.Length; i++)
        {
            if (_metric[i] > max_val)
            {
                max_val = _metric[i];
                max_idx = i;
            }
        }
        return max_idx;
    }

    // Display the battle field on the console
    private static void PrintField(int cell)
    {
        for (int x = 0; x < _width; x++)
        {
            for (int y = 0; y < _length; y++)
            {
                int c = Get1DCoord(x, y);
                if (c == cell)
                    Console.Out.Write(string.Format("[{0}]", _field[c]).PadLeft(4));
                else
                    Console.Out.Write(string.Format(" {0} ", _field[c]).PadLeft(4));
            }
            Console.Out.Write(" || ");
            for (int y = 0; y < _length; y++)
            {
                int c = Get1DCoord(x, y);
                if (c == cell)
                    Console.Out.Write(string.Format("[{0}]", _metric[c]).PadLeft(4));
                else
                    Console.Out.Write(string.Format(" {0} ", _metric[c]).PadLeft(4));
            }
            Console.Out.WriteLine();
        }
        Console.Out.WriteLine();
    }           
  }
}

รูปแบบการวางระเบิดที่เกิดขึ้นมีผลดังนี้ (ค่าฟิลด์ด้านซ้าย, ตัวชี้วัดทางด้านขวา)

Round 1:
  2   1   4   2   3   2   6  ||   7  16   8  10   4  18   6
  3   5   3   1   1   1   9  ||  11  18  18  21  17  28   5
  4  [2]  4   2   3   4   1  ||  19 [32] 21  20  17  24  22
  7   6   2   4   4   3   6  ||   8  17  20  14  16  22   8
  1   2   1   1   1   2   4  ||  14  15  14  11  13  16   7

Round 2:
  2   1   4   2   3   2   6  ||   5  13   6   9   4  18   6
  2   4   2   1   1  [1]  9  ||  10  15  17  19  17 [28]  5
  3   2   3   2   3   4   1  ||  16  24  18  17  17  24  22
  6   5   1   4   4   3   6  ||   7  14  19  12  16  22   8
  1   2   1   1   1   2   4  ||  12  12  12  10  13  16   7

Round 3:
  2   1   4   2   2   1   5  ||   5  13   6   7   3  15   5
  2   4   2   1   0   1   8  ||  10  15  17  16  14  20   2
  3  [2]  3   2   2   3   0  ||  16 [24] 18  15  16  21  21
  6   5   1   4   4   3   6  ||   7  14  19  11  14  19   6
  1   2   1   1   1   2   4  ||  12  12  12  10  13  16   7

Round 4:
  2   1   4   2   2   1   5  ||   3  10   4   6   3  15   5
  1   3   1   1   0   1   8  ||   9  12  16  14  14  20   2
  2   2   2   2   2  [3]  0  ||  13  16  15  12  16 [21] 21
  5   4   0   4   4   3   6  ||   6  11  18   9  14  19   6
  1   2   1   1   1   2   4  ||  10   9  10   9  13  16   7

Round 5:
  2   1   4   2   2   1   5  ||   3  10   4   6   2  13   3
  1   3   1   1   0  [0]  7  ||   9  12  16  13  12 [19]  2
  2   2   2   2   1   3   0  ||  13  16  15  10  14  15  17
  5   4   0   4   3   2   5  ||   6  11  18   7  13  17   6
  1   2   1   1   1   2   4  ||  10   9  10   8  11  13   5

Round 6:
  2   1   4   2   1   0   4  ||   3  10   4   5   2  11   2
  1   3   1   1   0   0   6  ||   9  12  16  11   8  13   0
  2   2   2   2   0   2   0  ||  13  16  15   9  14  14  15
  5   4  [0]  4   3   2   5  ||   6  11 [18]  6  11  15   5
  1   2   1   1   1   2   4  ||  10   9  10   8  11  13   5

Round 7:
  2   1   4   2   1   0   4  ||   3  10   4   5   2  11   2
  1   3   1   1   0   0   6  ||   8  10  13   9   7  13   0
  2  [1]  1   1   0   2   0  ||  11 [15] 12   8  12  14  15
  5   3   0   3   3   2   5  ||   3   8  10   3   8  15   5
  1   1   0   0   1   2   4  ||   8   8   7   7   9  13   5

Round 8:
  2   1   4   2   1   0   4  ||   1   7   2   4   2  11   2
  0   2   0   1   0   0   6  ||   7   7  12   7   7  13   0
  1   1   0   1   0   2   0  ||   8   8  10   6  12  14  15
  4   2   0   3   3  [2]  5  ||   2   6   8   2   8 [15]  5
  1   1   0   0   1   2   4  ||   6   6   6   7   9  13   5

Round 9:
  2   1   4   2   1   0   4  ||   1   7   2   4   2  11   2
  0   2   0   1   0   0   6  ||   7   7  12   7   6  12   0
  1   1   0   1   0  [1]  0  ||   8   8  10   5  10 [13] 13
  4   2   0   3   2   2   4  ||   2   6   8   0   6   9   3
  1   1   0   0   0   1   3  ||   6   6   6   5   8  10   4

Round 10:
  2   1   4   2   1   0   4  ||   1   7   2   4   2  10   1
  0   2  [0]  1   0   0   5  ||   7   7 [12]  7   6  11   0
  1   1   0   1   0   1   0  ||   8   8  10   4   8   9  10
  4   2   0   3   1   1   3  ||   2   6   8   0   6   8   3
  1   1   0   0   0   1   3  ||   6   6   6   4   6   7   2

Round 11:
  2   0   3   1   1   0   4  ||   0   6   0   3   0  10   1
  0   1   0   0   0  [0]  5  ||   4   5   5   5   3 [11]  0
  1   0   0   0   0   1   0  ||   6   8   6   4   6   9  10
  4   2   0   3   1   1   3  ||   1   5   6   0   5   8   3
  1   1   0   0   0   1   3  ||   6   6   6   4   6   7   2

Round 12:
  2   0   3   1   0   0   3  ||   0   6   0   2   1   7   1
  0   1   0   0   0   0   4  ||   4   5   5   4   1   7   0
  1   0   0   0   0  [0]  0  ||   6   8   6   4   5  [9]  8
  4   2   0   3   1   1   3  ||   1   5   6   0   4   7   2
  1   1   0   0   0   1   3  ||   6   6   6   4   6   7   2

Round 13:
  2   0   3   1   0   0   3  ||   0   6   0   2   1   6   0
  0   1   0   0   0   0   3  ||   4   5   5   4   1   6   0
  1  [0]  0   0   0   0   0  ||   6  [8]  6   3   3   5   5
  4   2   0   3   0   0   2  ||   1   5   6   0   4   6   2
  1   1   0   0   0   1   3  ||   6   6   6   3   4   4   0

Round 14:
  2   0   3   1   0  [0]  3  ||   0   5   0   2   1  [6]  0
  0   0   0   0   0   0   3  ||   2   5   4   4   1   6   0
  0   0   0   0   0   0   0  ||   4   4   4   3   3   5   5
  3   1   0   3   0   0   2  ||   0   4   5   0   4   6   2
  1   1   0   0   0   1   3  ||   4   4   5   3   4   4   0

Round 15:
  2   0   3   1   0   0   2  ||   0   5   0   2   1   4   0
  0   0   0   0   0   0   2  ||   2   5   4   4   1   4   0
  0   0   0   0   0   0   0  ||   4   4   4   3   3   4   4
  3   1   0   3   0  [0]  2  ||   0   4   5   0   4  [6]  2
  1   1   0   0   0   1   3  ||   4   4   5   3   4   4   0

Round 16:
  2  [0]  3   1   0   0   2  ||   0  [5]  0   2   1   4   0
  0   0   0   0   0   0   2  ||   2   5   4   4   1   4   0
  0   0   0   0   0   0   0  ||   4   4   4   3   3   3   3
  3   1   0   3   0   0   1  ||   0   4   5   0   3   3   1
  1   1   0   0   0   0   2  ||   4   4   5   3   3   3   0

Round 17:
  1   0   2   1   0   0   2  ||   0   3   0   1   1   4   0
  0   0   0   0   0   0   2  ||   1   3   3   3   1   4   0
  0   0   0   0   0   0   0  ||   4   4   4   3   3   3   3
  3   1  [0]  3   0   0   1  ||   0   4  [5]  0   3   3   1
  1   1   0   0   0   0   2  ||   4   4   5   3   3   3   0

Round 18:
  1   0   2   1   0   0   2  ||   0   3   0   1   1   4   0
  0   0   0   0   0   0   2  ||   1   3   3   3   1   4   0
  0   0   0   0   0   0   0  ||   3   3   2   2   2   3   3
  3  [0]  0   2   0   0   1  ||   0  [4]  2   0   2   3   1
  1   0   0   0   0   0   2  ||   2   4   2   2   2   3   0

Round 19:
  1   0   2   1   0  [0]  2  ||   0   3   0   1   1  [4]  0
  0   0   0   0   0   0   2  ||   1   3   3   3   1   4   0
  0   0   0   0   0   0   0  ||   2   2   2   2   2   3   3
  2   0   0   2   0   0   1  ||   0   2   2   0   2   3   1
  0   0   0   0   0   0   2  ||   2   2   2   2   2   3   0

Round 20:
  1  [0]  2   1   0   0   1  ||   0  [3]  0   1   1   2   0
  0   0   0   0   0   0   1  ||   1   3   3   3   1   2   0
  0   0   0   0   0   0   0  ||   2   2   2   2   2   2   2
  2   0   0   2   0   0   1  ||   0   2   2   0   2   3   1
  0   0   0   0   0   0   2  ||   2   2   2   2   2   3   0

Round 21:
  0   0   1   1   0   0   1  ||   0   1   0   0   1   2   0
  0   0   0   0   0   0   1  ||   0   1   2   2   1   2   0
  0   0   0   0   0   0   0  ||   2   2   2   2   2   2   2
  2   0   0   2   0  [0]  1  ||   0   2   2   0   2  [3]  1
  0   0   0   0   0   0   2  ||   2   2   2   2   2   3   0

Round 22:
  0   0   1   1   0   0   1  ||   0   1   0   0   1   2   0
  0   0   0   0   0   0   1  ||   0   1   2   2   1   2   0
 [0]  0   0   0   0   0   0  ||  [2]  2   2   2   2   1   1
  2   0   0   2   0   0   0  ||   0   2   2   0   2   1   1
  0   0   0   0   0   0   1  ||   2   2   2   2   2   1   0

Round 23:
  0   0   1   1   0   0   1  ||   0   1   0   0   1   2   0
  0   0  [0]  0   0   0   1  ||   0   1  [2]  2   1   2   0
  0   0   0   0   0   0   0  ||   1   1   2   2   2   1   1
  1   0   0   2   0   0   0  ||   0   1   2   0   2   1   1
  0   0   0   0   0   0   1  ||   1   1   2   2   2   1   0

Round 24:
  0   0   0   0   0   0   1  ||   0   0   0   0   0   2   0
  0   0   0   0   0   0   1  ||   0   0   0   0   0   2   0
  0   0  [0]  0   0   0   0  ||   1   1  [2]  2   2   1   1
  1   0   0   2   0   0   0  ||   0   1   2   0   2   1   1
  0   0   0   0   0   0   1  ||   1   1   2   2   2   1   0

Round 25:
  0   0   0   0   0  [0]  1  ||   0   0   0   0   0  [2]  0
  0   0   0   0   0   0   1  ||   0   0   0   0   0   2   0
  0   0   0   0   0   0   0  ||   1   1   1   1   1   1   1
  1   0   0   1   0   0   0  ||   0   1   1   0   1   1   1
  0   0   0   0   0   0   1  ||   1   1   1   1   1   1   0

Round 26:
  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   0   0   0
 [0]  0   0   0   0   0   0  ||  [1]  1   1   1   1   0   0
  1   0   0   1   0   0   0  ||   0   1   1   0   1   1   1
  0   0   0   0   0   0   1  ||   1   1   1   1   1   1   0

Round 27:
  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   0   0   0
  0   0  [0]  0   0   0   0  ||   0   0  [1]  1   1   0   0
  0   0   0   1   0   0   0  ||   0   0   1   0   1   1   1
  0   0   0   0   0   0   1  ||   0   0   1   1   1   1   0

Round 28:
  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   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   0   0   0   0  [1]  1
  0   0   0   0   0   0   1  ||   0   0   0   0   0   1   0

Done in 28 rounds

1

สิ่งนี้สามารถแก้ไขได้โดยใช้ต้นไม้ที่มีความลึก O (3 ^ (n)) โดยที่ n คือผลรวมของกำลังสองทั้งหมด

ก่อนอื่นให้พิจารณาว่าเป็นการแก้ปัญหาเล็กน้อยด้วยต้นไม้ O (9 ^ n) เพียงแค่พิจารณาสถานที่วางระเบิดที่เป็นไปได้ทั้งหมด ตัวอย่างเช่นดูการดำเนินงานของ Alfe

ต่อไปตระหนักว่าเราสามารถทำงานเพื่อวางระเบิดจากล่างขึ้นบนและยังคงได้รับรูปแบบการทิ้งระเบิดขั้นต่ำ

  1. เริ่มจากมุมซ้ายล่าง
  2. วางระเบิดเพื่อให้อภัยด้วยบทละครเดียวที่มีเหตุผล (ขึ้นและลงไปทางขวา)
  3. ย้ายหนึ่งตารางไปทางขวา
  4. ในขณะที่เป้าหมายมีค่ามากกว่าศูนย์ให้พิจารณาแต่ละบทละคร 2 บทที่ตรง (ขึ้นหรือลงและไปทางขวา) ลดมูลค่าของเป้าหมายหนึ่งและสร้างสาขาใหม่สำหรับแต่ละความเป็นไปได้
  5. ย้ายไปทางขวา
  6. ในขณะที่เป้าหมายมีค่ามากกว่าศูนย์ให้พิจารณาแต่ละบทละครทั้ง 3 บทที่ดูสมเหตุสมผล (ซ้ายขึ้นขึ้นและลงขวา) ลดมูลค่าของเป้าหมายหนึ่งและสร้างสาขาใหม่สำหรับแต่ละความเป็นไปได้
  7. ทำซ้ำขั้นตอนที่ 5 และ 6 จนกระทั่งแถวถูกกำจัด
  8. เลื่อนขึ้นเป็นแถวแล้วทำซ้ำขั้นตอนที่ 1 ถึง 7 จนกระทั่งไขปริศนาถูกแก้ไข

อัลกอริทึมนี้ถูกต้องเพราะ

  1. มีความจำเป็นต้องทำให้แต่ละแถวเสร็จสมบูรณ์ในบางจุด
  2. การทำแถวให้สมบูรณ์จะต้องมีการเล่นอย่างใดอย่างหนึ่งข้างต้นด้านล่างหรือภายในแถวนั้น
  3. มันเป็นการดีหรือดีกว่าเสมอในการเลือกละครที่อยู่เหนือแถวที่มีการล้างต่ำกว่าการเล่นในแถวหรือด้านล่างของแถว

ในทางปฏิบัติอัลกอริธึมนี้จะทำดีกว่าค่าสูงสุดตามทฤษฎีอย่างสม่ำเสมอเพราะมันจะระเบิดเพื่อนบ้านเป็นประจำและลดขนาดของการค้นหา หากเราสมมติว่าการทิ้งระเบิดแต่ละครั้งจะลดมูลค่าของ 4 เป้าหมายเพิ่มเติมอัลกอริทึมของเราจะทำงานใน O (3 ^ (n / 4)) หรือประมาณ O (1.3 ^ n)

เนื่องจากอัลกอริทึมนี้ยังคงเป็นเลขชี้กำลังคุณควรจำกัดความลึกของการค้นหา เราอาจ จำกัด จำนวนสาขาที่อนุญาตให้บางหมายเลข, X, และเมื่อเราอยู่ลึกนี้เราจะบังคับให้อัลกอริทึมเลือกเส้นทางที่ดีที่สุดที่ได้ระบุจนถึงตอนนี้ (อันที่มีผลรวมบอร์ดขั้นต่ำทั้งหมดในเทอร์มินัลใบหนึ่ง ) จากนั้นอัลกอริทึมของเรารับประกันว่าจะทำงานในเวลา O (3 ^ X) แต่ไม่รับประกันว่าจะได้รับคำตอบที่ถูกต้อง อย่างไรก็ตามเราสามารถเพิ่ม X และทดสอบเชิงประจักษ์ได้เสมอหากการแลกเปลี่ยนระหว่างการคำนวณที่เพิ่มขึ้นและคำตอบที่ดีกว่านั้นคุ้มค่า


1

ฟังก์ชั่นการประเมินผลรวมทั้งหมด:

int f (int ** matrix, int width, int height, int x, int y)
{
    int m[3][3] = { 0 };

    m[1][1] = matrix[x][y];
    if (x > 0) m[0][1] = matrix[x-1][y];
    if (x < width-1) m[2][1] = matrix[x+1][y];

    if (y > 0)
    {
        m[1][0] = matrix[x][y-1];
        if (x > 0) m[0][0] = matrix[x-1][y-1];
        if (x < width-1) m[2][0] = matrix[x+1][y-1];
    }

    if (y < height-1)
    {
        m[1][2] = matrix[x][y+1];
        if (x > 0) m[0][2] = matrix[x-1][y+1];
        if (x < width-1) m[2][2] = matrix[x+1][y+1];
    }

    return m[0][0]+m[0][1]+m[0][2]+m[1][0]+m[1][1]+m[1][2]+m[2][0]+m[2][1]+m[2][2];
}

ฟังก์ชั่นวัตถุประสงค์:

Point bestState (int ** matrix, int width, int height)
{
    Point p = new Point(0,0);
    int bestScore = 0;
    int b = 0;

    for (int i=0; i<width; i++)
        for (int j=0; j<height; j++)
        {
            b = f(matrix,width,height,i,j);

            if (b > bestScore)
            {
                bestScore = best;
                p = new Point(i,j);
            }
        }

    retunr p;
}

ฟังก์ชั่นทำลาย:

void destroy (int ** matrix, int width, int height, Point p)
{
    int x = p.x;
    int y = p.y;

    if(matrix[x][y] > 0) matrix[x][y]--;
    if (x > 0) if(matrix[x-1][y] > 0) matrix[x-1][y]--;
    if (x < width-1) if(matrix[x+1][y] > 0) matrix[x+1][y]--;

    if (y > 0)
    {
        if(matrix[x][y-1] > 0) matrix[x][y-1]--;
        if (x > 0) if(matrix[x-1][y-1] > 0) matrix[x-1][y-1]--;
        if (x < width-1) if(matrix[x+1][y-1] > 0) matrix[x+1][y-1]--;
    }

    if (y < height-1)
    {
        if(matrix[x][y] > 0) matrix[x][y+1]--;
        if (x > 0) if(matrix[x-1][y+1] > 0) matrix[x-1][y+1]--;
        if (x < width-1) if(matrix[x+1][y+1] > 0) matrix[x+1][y+1]--;
    }
}

ฟังก์ชั่นเป้าหมาย:

bool isGoal (int ** matrix, int width, int height)
{
    for (int i=0; i<width; i++)
        for (int j=0; j<height; j++)
            if (matrix[i][j] > 0)
                return false;
    return true;
}

ฟังก์ชันขยายเชิงเส้น:

void solve (int ** matrix, int width, int height)
{
    while (!isGoal(matrix,width,height))
    {
        destroy(matrix,width,height, bestState(matrix,width,height));
    }
}

สิ่งนี้ไม่ดีที่สุด แต่สามารถปรับให้เหมาะสมผ่านการค้นหาฟังก์ชันการประเมินที่ดีกว่า

.. แต่คิดเกี่ยวกับปัญหานี้ฉันคิดว่าหนึ่งในประเด็นหลักคือการถูกทิ้งตัวเลขในช่วงกลางของศูนย์ในบางจุดดังนั้นฉันจะใช้วิธีอื่น .. ซึ่งครอบงำค่าน้อยที่สุดเป็นศูนย์แล้วลอง ยกเว้นศูนย์เท่าที่จะเป็นไปได้ซึ่งนำไปสู่การลดขนาดค่าทั่วไปที่มีอยู่ให้น้อยที่สุด


0

ปัญหาทั้งหมดนี้ทำให้เดือดร้อนถึงการคำนวณระยะทางแก้ไข เพียงคำนวณความแตกต่างของระยะทาง Levenshtein ระหว่างเมทริกซ์ที่กำหนดและเมทริกซ์ศูนย์ที่ซึ่งการแก้ไขจะถูกแทนที่ด้วยการทิ้งระเบิดโดยใช้การเขียนโปรแกรมแบบไดนามิกเพื่อจัดเก็บระยะทางระหว่างอาร์เรย์กลาง ฉันแนะนำให้ใช้แฮชของเมทริกซ์เป็นกุญแจ ในหลอก -Python:

memo = {}

def bomb(matrix,i,j):
    # bomb matrix at i,j

def bombsRequired(matrix,i,j):
    # bombs required to zero matrix[i,j]

def distance(m1, i, len1, m2, j, len2):
    key = hash(m1)
    if memo[key] != None: 
        return memo[key]

    if len1 == 0: return len2
    if len2 == 0: return len1

    cost = 0
    if m1 != m2: cost = m1[i,j]
    m = bomb(m1,i,j)
    dist = distance(str1,i+1,len1-1,str2,j+1,len2-1)+cost)
    memo[key] = dist
    return dist

0

นี่คือคำตอบของคำถามที่ถามแรก ฉันไม่ได้สังเกตว่าเขาเปลี่ยนพารามิเตอร์

สร้างรายการเป้าหมายทั้งหมด กำหนดค่าให้กับเป้าหมายตามจำนวนของค่าบวกที่ได้รับผลกระทบจากการปล่อย (ตัวมันเองและเพื่อนบ้านทั้งหมด) ค่าสูงสุดจะเป็นเก้า

จัดเรียงเป้าหมายตามจำนวนของเป้าหมายที่ได้รับผลกระทบ (มากไปหาน้อย) โดยมีการเรียงลำดับจากมากไปน้อยในผลรวมของแต่ละเป้าหมายที่ได้รับผลกระทบ

วางระเบิดบนเป้าหมายที่มีอันดับสูงสุดจากนั้นคำนวณเป้าหมายใหม่และทำซ้ำจนกว่าค่าเป้าหมายทั้งหมดจะเป็นศูนย์

เห็นด้วยนี่ไม่ใช่สิ่งที่ดีที่สุดเสมอไป ตัวอย่างเช่น,

100011
011100
011100
011100
000000
100011

วิธีนี้จะใช้เวลา 5 ระเบิดเพื่อล้าง อย่างดีที่สุดแม้ว่าคุณสามารถทำได้ใน 4 ยังคงใกล้สวยและไม่มีการย้อนรอย สำหรับสถานการณ์ส่วนใหญ่มันจะดีที่สุดหรือใกล้มาก

การใช้หมายเลขปัญหาดั้งเดิมวิธีการนี้แก้ได้ใน 28 ระเบิด

การเพิ่มรหัสเพื่อสาธิตวิธีการนี้ (ใช้แบบฟอร์มด้วยปุ่ม):

         private void button1_Click(object sender, EventArgs e)
    {
        int[,] matrix = new int[10, 10] {{5, 20, 7, 1, 9, 8, 19, 16, 11, 3}, 
                                         {17, 8, 15, 17, 12, 4, 5, 16, 8, 18},
                                         { 4, 19, 12, 11, 9, 7, 4, 15, 14, 6},
                                         { 17, 20, 4, 9, 19, 8, 17, 2, 10, 8},
                                         { 3, 9, 10, 13, 8, 9, 12, 12, 6, 18}, 
                                         {16, 16, 2, 10, 7, 12, 17, 11, 4, 15},
                                         { 11, 1, 15, 1, 5, 11, 3, 12, 8, 3},
                                         { 7, 11, 16, 19, 17, 11, 20, 2, 5, 19},
                                         { 5, 18, 2, 17, 7, 14, 19, 11, 1, 6},
                                         { 13, 20, 8, 4, 15, 10, 19, 5, 11, 12}};


        int value = 0;
        List<Target> Targets = GetTargets(matrix);
        while (Targets.Count > 0)
        {
            BombTarget(ref matrix, Targets[0]);
            value += 1;
            Targets = GetTargets(matrix);
        }
        Console.WriteLine( value);
        MessageBox.Show("done: " + value);
    }

    private static void BombTarget(ref int[,] matrix, Target t)
    {
        for (int a = t.x - 1; a <= t.x + 1; a++)
        {
            for (int b = t.y - 1; b <= t.y + 1; b++)
            {
                if (a >= 0 && a <= matrix.GetUpperBound(0))
                {
                    if (b >= 0 && b <= matrix.GetUpperBound(1))
                    {
                        if (matrix[a, b] > 0)
                        {
                            matrix[a, b] -= 1;
                        }
                    }
                }
            }
        }
        Console.WriteLine("Dropped bomb on " + t.x + "," + t.y);
    }

    private static List<Target> GetTargets(int[,] matrix)
    {
        List<Target> Targets = new List<Target>();
        int width = matrix.GetUpperBound(0);
        int height = matrix.GetUpperBound(1);
        for (int x = 0; x <= width; x++)
        {
            for (int y = 0; y <= height; y++)
            {
                Target t = new Target();
                t.x = x;
                t.y = y;
                SetTargetValue(matrix, ref t);
                if (t.value > 0) Targets.Add(t);
            }
        }
        Targets = Targets.OrderByDescending(x => x.value).ThenByDescending( x => x.sum).ToList();
        return Targets;
    }

    private static void SetTargetValue(int[,] matrix, ref Target t)
    {
        for (int a = t.x - 1; a <= t.x + 1; a++)
        {
            for (int b = t.y - 1; b <= t.y + 1; b++)
            {
                if (a >= 0 && a <= matrix.GetUpperBound(0))
                {
                    if (b >= 0 && b <= matrix.GetUpperBound(1))
                    {
                        if (matrix[ a, b] > 0)
                        {
                            t.value += 1;
                            t.sum += matrix[a,b];
                        }

                    }
                }
            }
        }

    }

ชั้นเรียนที่คุณต้องการ:

        class Target
    {
        public int value;
        public int sum;
        public int x;
        public int y;
    }

1
ไม่เหมาะสม ตัวอย่าง: 09090วิธีการนี้ต้องใช้ระเบิด 18 ลูก สามารถทำได้ใน 9
Mysticial

@Mysticial คุณไม่ได้อ่านคำตอบอย่างละเอียด เนื่องจากมันขึ้นอยู่กับจำนวนของเขตข้อมูลที่ไม่เป็นศูนย์ที่ได้รับผลกระทบอัลกอริทึมนี้จะระเบิดศูนย์กลางและจะทำใน 9 หยด ค่าสูงถึง 9 เป็นเพราะมีถึงแปดเพื่อนบ้านและตัวเอง
Anthony Queen

ถ้าเช่นนั้นจะ1010101เป็น0010100อย่างไร
ลึกลับ

@Master อย่างเป็นทางการสำหรับ First, Zero แรกจากนั้น Zero สุดท้ายจะถูกโจมตี มันจะเป็นสองหยด นั่นเป็นเพราะทุกครั้งที่มันวางระเบิดมันจะคำนวณเป้าหมายที่ดีที่สุดต่อไป สำหรับตัวอย่างที่ผ่านมาศูนย์ตรงกลางอีกครั้ง; หนึ่งหยด.
Anthony Queen

1
@AnthonyQueen: มันไม่ทำงาน โปรดดูchat.stackoverflow.com/transcript/message/8224273#8224273สำหรับตัวอย่างของฉัน
nneonneo
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.