Haskell , 228 227 225 224 ไบต์
import Data.List
z=zipWith
a!b=div(max(a*a)(a*b))a
l x=z(!)(z(!)x(0:x))$tail x++[0]
s=(\x->length.($x).filter<$>[(>0),(<0)]).nub.(>>=id).(until=<<((==)=<<))((.)>>=id$transpose.map l).z(\i->z(\j x->2^i*j*(2*x-1))[1,3..])[1..]
ลองออนไลน์!
คำอธิบาย:
ความคิดสำหรับการแก้ปัญหานี้จะเป็นดังนี้: initialise เมทริกซ์ที่มีค่าที่ไม่ซ้ำกันในแต่ละเซลล์ในเชิงบวกสำหรับและเชิงลบสำหรับ1
0
จากนั้นเปรียบเทียบแต่ละเซลล์กับเพื่อนบ้านเป็นประจำและหากเพื่อนบ้านมีสัญลักษณ์เดียวกัน แต่มีตัวเลขที่มีค่าสัมบูรณ์ที่ใหญ่กว่าให้แทนที่หมายเลขเซลล์ด้วยหมายเลขของเพื่อนบ้าน เมื่อถึงจุดคงที่ให้นับจำนวนของจำนวนบวกที่แตกต่างกันสำหรับจำนวนของ1
ภูมิภาคและจำนวนลบที่แตกต่างกันสำหรับจำนวนของ0
ภูมิภาค
ในรหัส:
s=(\x->length.($x).filter<$>[(>0),(<0)]).nub.(>>=id).(until=<<((==)=<<))((.)>>=id$transpose.map l).z(\i->z(\j x->2^i*j*(2*x-1))[1,3..])[1..]
สามารถแยกออกเป็นการประมวลผลล่วงหน้า (การกำหนดหมายเลขให้กับเซลล์) การวนซ้ำและการประมวลผลภายหลัง (การนับเซลล์)
กระบวนการเตรียมการผลิต
ส่วนการประมวลผลล่วงหน้าคือฟังก์ชั่น
z(\i->z(\j x->2^i*j*(2*x-1))[1,3..])[1..]
ซึ่งใช้z
เป็นตัวย่อสำหรับzipWith
การโกนไม่กี่ไบต์ สิ่งที่เราทำที่นี่คือซิปอาเรย์สองมิติพร้อมดัชนีจำนวนเต็มที่แถวและดัชนีเลขจำนวนเต็มคี่ที่คอลัมน์ เราทำเช่นนี้เนื่องจากเราสามารถสร้างจำนวนเต็มไม่ซ้ำกันจากคู่ของจำนวนเต็มโดยใช้สูตรที่(i,j)
(2^i)*(2j+1)
หากเราสร้างจำนวนเต็มคี่สำหรับj
เราสามารถข้ามการคำนวณ2*j+1
และบันทึกสามไบต์
ด้วยหมายเลขที่ไม่ซ้ำกันตอนนี้เราต้องคูณด้วยเครื่องหมายตามค่าในเมทริกซ์ซึ่งได้มาเป็น 2*x-1
การย้ำ
การวนซ้ำทำได้โดย
(until=<<((==)=<<))((.)>>=id$transpose.map l)
เนื่องจากอินพุตอยู่ในรูปแบบของรายการเราจึงทำการเปรียบเทียบเพื่อนบ้านในแต่ละแถวย้ายเมทริกซ์ทำการเปรียบเทียบในแต่ละแถวอีกครั้ง (ซึ่งเนื่องจากการแปลงเป็นคอลัมน์ก่อนหน้านี้) และเปลี่ยนอีกครั้ง รหัสที่สำเร็จหนึ่งในขั้นตอนเหล่านี้คือ
((.)>>=id$transpose.map l)
โดยที่l
ฟังก์ชั่นการเปรียบเทียบ (รายละเอียดด้านล่าง) และtranspose.map l
ดำเนินการครึ่งหนึ่งของขั้นตอนการเปรียบเทียบและการขนย้าย (.)>>=id
ดำเนินการโต้แย้งสองครั้งเป็นรูปแบบ pointfree \f -> f.f
และสั้นลงหนึ่งไบต์ในกรณีนี้เนื่องจากกฎที่มีความสำคัญกว่าผู้ประกอบการ
l
l x=z(!)(z(!)x(0:x))$tail x++[0]
ถูกกำหนดไว้ในแถวด้านบนเป็น รหัสนี้ดำเนินการตัวดำเนินการเปรียบเทียบ(!)
(ดูด้านล่าง) ในทุกเซลล์ที่มีเพื่อนบ้านด้านซ้ายก่อนแล้วจึงมีเพื่อนบ้านที่เหมาะสมโดยการซิปรายการที่x
มีรายการที่เลื่อนด้านขวา0:x
และรายการที่เลื่อนด้านซ้ายtail x++[0]
ในทางกลับกัน เราใช้ศูนย์เพื่อรองรายการที่ถูกเลื่อนเนื่องจากมันไม่สามารถเกิดขึ้นได้ในเมทริกซ์ที่ประมวลผลล่วงหน้า
a!b
a!b=div(max(a*a)(a*b))a
ถูกกำหนดไว้ในแถวข้างต้นนี้เป็น สิ่งที่เราต้องการทำที่นี่คือความแตกต่างกรณีดังต่อไปนี้:
- หาก
sgn(a) = -sgn(b)
เรามีพื้นที่ตรงข้ามสองแห่งในเมทริกซ์และไม่ต้องการรวมมันเข้าด้วยกันดังนั้นa
ไม่เปลี่ยนแปลง
- หาก
sgn(b) = 0
เรามีกรณีมุมซึ่งb
เป็นช่องว่างภายในและดังนั้นจึงa
ยังคงไม่เปลี่ยนแปลง
- หาก
sgn(a) = sgn(b)
เราต้องการรวมสองพื้นที่และนำสิ่งที่มีค่าสัมบูรณ์ที่ใหญ่กว่า (เพื่อประโยชน์ของความสะดวกสบาย)
โปรดทราบว่าไม่สามารถจะsgn(a)
0
เราทำสิ่งนี้สำเร็จด้วยสูตรที่กำหนด หากมีอาการของa
และb
แตกต่างกันa*b
น้อยหรือเท่ากับศูนย์ในขณะที่a*a
อยู่เสมอมากกว่าศูนย์เพื่อให้เรารับมันเป็นสูงสุดและหารด้วยที่จะได้รับกลับมาa
a
มิฉะนั้นmax(a*a)(a*b)
เป็นabs(a)*max(abs(a),(abs(b))
และโดยการหารด้วยa
เราได้sgn(a)*max(abs(a),abs(b))
ซึ่งเป็นจำนวนที่มีค่าสัมบูรณ์ที่ใหญ่กว่า
ในการวนซ้ำฟังก์ชัน((.)>>=id$transpose.map l)
จนกว่าจะถึงจุดคงที่เราใช้(until=<<((==)=<<))
ซึ่งนำมาจากคำตอบสแต็คโอเวอร์โฟลว์นี้นี้
postprocessing
สำหรับการประมวลผลภายหลังเราใช้ชิ้นส่วน
(\x->length.($x).filter<$>[(>0),(<0)]).nub.(>>=id)
ซึ่งเป็นเพียงชุดของขั้นตอน
(>>=id)
สควอชรายการของรายการลงในรายการเดียว
nub
กำจัดคู่ผสม
(\x->length.($x).filter<$>[(>0),(<0)])
พาร์ทิชันรายการเป็นคู่ของรายการหนึ่งรายการสำหรับบวกและอีกหนึ่งสำหรับตัวเลขลบและคำนวณความยาวของพวกเขา
[[1,0];[0,1]]
เพื่อให้แน่ใจว่าไม่มีการเชื่อมต่อในแนวทแยง