ค้นหาพื้นที่ของรูปหลายเหลี่ยมนูนที่ใหญ่ที่สุด


28

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

  • จุดสุดยอดทุกอันอยู่ในรายการ
  • ไม่มีองค์ประกอบของรายการอยู่ในรูปหลายเหลี่ยม

ตัวอย่าง:

(0, 0) (8, 0) (0, 1) (3, 1) (7, 1) (1, 2) (5, 2) (9, 2) (9, 2) (2, 3) (5, 3) (7, 3) (3, 4) (5, 5) (11, 5)

มองเห็น:

o       o
o  o   o
 o   o   o
  o  o o
   o
     o     o

รูปหลายเหลี่ยมนูนที่ใหญ่ที่สุดที่คุณสามารถทำได้คือ:

o     
o  o  
 o   o
  o  o
   o
     o

ด้วยพื้นที่ 12


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

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

รหัสที่สั้นที่สุดในหน่วยไบต์ชนะ


คุณรู้อัลกอริทึมที่รวดเร็วสำหรับกรณีที่แย่ที่สุดหรือไม่?
xnor

3
หากคุณต้องการบังคับใช้การ จำกัด เวลาใน 100 จุดยอดคุณอาจต้องมีกรณีทดสอบอย่างน้อยหนึ่งกรณี (หลายกรณีเช่นหนึ่งที่ 100 จุดยอดทั้งหมดเป็นส่วนหนึ่งของการแก้ปัญหาจุดหนึ่งอยู่ที่ 99 จุดและเพียง 10 จุดเท่านั้น) .
Martin Ender

@ MartinBüttnerน่าเสียดายที่ฉันไม่สามารถสร้างกรณีทดสอบนี้เนื่องจากฉันไม่ได้ใช้งานด้วยตนเอง ปัญหาค่อนข้างยุ่งยาก :)
orlp

@xnor ตัวอย่างคู่สามารถพบได้ที่นี่
orlp

"ปัดเป็นทศนิยมไม่น้อยกว่า 2 หลักหลังจุดทศนิยม"?
DavidC

คำตอบ:


12

Javascript ES6, 738 ไบต์

((V,C,L,r,k,n,A,G,F,e,i,j,q)=>p=>{p=p.map((p,i)=>({i:i,x:p[0],y:p[1]}));A=(f,p,a,b,v,i)=>{for(i=p[n],v=V(a,b);i--;)if(f(v,V(a,p[i])))return 1};G=(p,i,a)=>{for(i=p[n]-1,a=C(p[i],p[0]);i--;)a+=C(p[i],p[i+1]);if((a/=2)>r)r=a};F=(p,s,l,f,a,b,v)=>(l=s[n],f=s[0],a=s[l-2],b=s[l-1],e[a.i][b.i]||A((a,b)=>C(a,b)?0:a.x<0==b.x<0&&a.y<0==b.y<0&&L(a)>L(b),p,a,b)?0:(p=(v=V(a,b),p[k](x=>C(v,V(a,x))>=0)),A((a,b)=>C(a,b)>0,p,b,f)?0:(p.map(q=>F(p[k](r=>q!==r),[...s,q])),s[2]&&!p[n]&&!e[b.i][f.i]?G(s):0)));e=p.map(x=>p.map(y=>x===y));for(i=p[n];i--;){for(j=i;j--;){q=p[k]((p,x)=>x-i&&x-j);F(q,[p[i],p[j]]);F(q,[p[j],p[i]]);e[i][j]=e[j][i]=1}}console.log(r)})((a,b)=>({x:b.x-a.x,y:b.y-a.y}),(a,b)=>a.x*b.y-a.y*b.x,v=>v.x*v.x+v.y*v.y,0,'filter','length')

นี่คือ ES5 หรือเวอร์ชันต่ำกว่าที่ควรทำงานในเบราว์เซอร์และโหนดส่วนใหญ่โดยไม่ต้องปรับแต่ง: 827 bytes

eval("(%V,C,L,r,k,n,A,G,F,e,i,j,q){@%p){p=p.map(%p,i){@{i:i,x:p[0],y:p[1]}});A=%f,p,a,b,v,i){for(i=p[n],v=V(a,b);i--;)if(f(v,V(a,p[i])))@1};G=%p,i,a){for(i=p[n]-1,a=C(p[i],p[0]);i--;)a+=C(p[i],p[i+1]);if((a/=2)>r)r=a};F=%p,s,l,f,a,b,v){@(l=s[n],f=s[0],a=s[l-2],b=s[l-1],e[a.i][b.i]||A(%a,b){@C(a,b)!=0?0:a.x<0==b.x<0&&a.y<0==b.y<0&&L(a)>L(b)},p,a,b)?0:(p=(v=V(a,b),p[k](%x){@C(v,V(a,x))>=0})),A(%a,b){@C(a,b)>0},p,b,f)?0:(p.forEach(%q){@F(p[k](%r){@q!==r}),s.concat([q]))}),s[2]&&p[n]==0&&!e[b.i][f.i]?G(s):0)))};e=p.map(%x,i){@p.map(%y,j){@i==j})});for(i=p[n];i--;){for(j=i;j--;){q=p[k](%p,x){@x!=i&&x!=j});F(q,[p[i],p[j]]);F(q,[p[j],p[i]]);e[i][j]=e[j][i]=1}}console.log(r)}})(%a,b){@{x:b.x-a.x,y:b.y-a.y}},%a,b){@a.x*b.y-a.y*b.x},%v){@v.x*v.x+v.y*v.y},0,'filter','length')".replace(/%/g,'function(').replace(/@/g,'return '))

โค้ดส่งคืนฟังก์ชันที่ไม่ระบุชื่อ [[0,1],[2,3],[4,5]]เป็นพารามิเตอร์ก็จะใช้เวลาอาร์เรย์ของจุดที่เหมือน หากต้องการใช้คุณสามารถวางไว้var f=ก่อนหน้าหรือถ้าคุณต้องการใช้จากบรรทัดคำสั่งเพิ่ม(process.argv[2].replace(/ /g,'').slice(1,-1).split(')(').map((x)=>x.split(',')))ไปยังจุดสิ้นสุดและเรียกมันว่าชอบnode convpol.js '(1,2)(3,4)(5,6)'

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

อัลกอริทึมนั้นคล้ายกับการสแกนของ Grahamยกเว้นว่าจะต้องค้นหาเปลือกนูนที่ว่างเปล่าทุกที่

คำอธิบาย

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

  1. เริ่มด้วยจุดคู่หนึ่งและรายการคะแนนอื่น ๆ ทั้งหมด
  2. หากจุดคู่ปัจจุบันไปถึงจุดใด ๆ ในรายการให้หยุด
  3. กรองทุกจุดตามเข็มนาฬิกาของคู่ปัจจุบันเนื่องจากจะทำให้รูปหลายเหลี่ยมเว้า
  4. สำหรับทุกแต้มที่เหลือให้ทำดังนี้:
    1. หากเส้นจากจุดนี้ไปยังจุดแรกของห่วงโซ่ผ่านหรือล้อมรอบจุดใด ๆ ทวนเข็มนาฬิกาข้ามจุดนี้เนื่องจากรูปหลายเหลี่ยมใด ๆ จะล้อมรอบจุด
    2. เพิ่มจุดนี้ในห่วงโซ่เรียกเก็บจากขั้นตอนที่ 1 ด้วยห่วงโซ่ปัจจุบันและรายการของจุด
  5. หากไม่มีคะแนนเหลือและโซ่มีอย่างน้อย 3 คะแนนนี่เป็นรูปหลายเหลี่ยมนูนที่ถูกต้อง จดจำพื้นที่ที่ใหญ่ที่สุดของรูปหลายเหลี่ยมเหล่านี้

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

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


2
+1 นี่คือคำตอบที่น่าอัศจรรย์ คุณอาจจะสามารถที่จะเปลี่ยน===และ!==ด้วย==และ!=แต่ฉันไม่สามารถให้แน่ใจว่าไม่เข้าใจรหัสของคุณ ...
jrich

1
ขอบคุณ! โดยเฉพาะ === และ! == กำลังเปรียบเทียบวัตถุดังนั้นไม่เลยเศร้า มันใช้ในการเปรียบเทียบดัชนี แต่(x,i)=>p.i==i(13 ตัวอักษร) ค่อนข้างนานกว่าx=>p===x(8 ตัวอักษร) แม้หลังจากปรับให้เหมาะสมแล้ว
ricochet1k

2
มีคำอธิบายสำหรับคุณ @Lembik
ricochet1k

1
คุณดูเหมือนจะพ่ายแพ้บันทึก O (n ^ 3) ที่กล่าวถึงในความคิดเห็นของคำถาม SO ที่เชื่อมโยง!

1
เอาล่ะฉันไปถึงที่ที่ฉันไม่เชื่อว่ามันเป็นไปได้ที่สิ่งนี้จะวิ่งน้อยกว่า O (n ^ 3) ฉันใหม่มากต่อความซับซ้อนของอัลกอริทึม
ricochet1k
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.