Tìm diện tích của đa giác lồi lớn nhất


28

Đưa ra một danh sách các tọa độ nguyên, tìm diện tích của đa giác lồi lớn nhất mà bạn có thể xây dựng từ danh sách sao cho -

  • mọi đỉnh đều nằm trong danh sách
  • không có phần tử nào của danh sách được chứa trong đa giác.

Thí dụ:

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

Hình dung:

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

Đa giác lồi lớn nhất bạn có thể thực hiện từ đây là:

o     
o  o  
 o   o
  o  o
   o
     o

Với diện tích 12.


Bạn có thể lấy danh sách tọa độ theo bất kỳ định dạng hợp lý nào và nên xuất ra (theo cách phù hợp với ngôn ngữ bạn chọn) khu vực của đa giác lồi lớn nhất, được làm tròn đến không dưới 2 chữ số sau dấu thập phân.

Ngoài ra, bạn phải sử dụng một số loại thuật toán, và không chỉ đơn giản là vũ phu tất cả các tập hợp con của các điểm. Để thực thi điều này, chương trình của bạn phải giải quyết một danh sách 50 đỉnh trong dưới một phút trên PC hiện đại.

Mã ngắn nhất trong byte thắng.


Bạn có biết một thuật toán nhanh cho trường hợp xấu nhất?
xnor

3
Nếu bạn muốn thực thi giới hạn thời gian trên 100 đỉnh, có lẽ bạn nên bao gồm ít nhất một trường hợp thử nghiệm như vậy (lý tưởng là một số, ví dụ một trong đó tất cả 100 đỉnh là một phần của giải pháp, một trong đó 99 và một trong đó chỉ có 10) .
Martin Ender

@ MartinBüttner Đáng buồn thay, tôi không thể tạo trường hợp thử nghiệm này vì bản thân tôi không có triển khai công việc. Vấn đề khá khó khăn :)
orlp

@xnor Một vài ví dụ có thể được tìm thấy ở đây .
orlp

"Làm tròn đến không ít hơn 2 chữ số sau dấu thập phân"?
DavidC

Câu trả lời:


12

Javascript ES6, 738 byte

((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')

Đây là phiên bản ES5 trở xuống nên hoạt động trong hầu hết các trình duyệt và nút mà không cần điều chỉnh: 827 byte

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 '))

Mã trả về một hàm ẩn danh. Là một tham số, nó cần một mảng các điểm, như [[0,1],[2,3],[4,5]]. Để sử dụng nó, bạn có thể đặt var f=trước nó hoặc nếu bạn muốn sử dụng nó từ dòng lệnh, thêm (process.argv[2].replace(/ /g,'').slice(1,-1).split(')(').map((x)=>x.split(',')))vào cuối và gọi nó như thế nàonode convpol.js '(1,2)(3,4)(5,6)'

Cảm ơn các thử thách! Vì không có triển khai tham chiếu, tôi không thể chứng minh điều này là đúng, nhưng ít nhất nó phù hợp với hoán vị của danh sách điểm. Tôi gần như không nghĩ rằng điều này sẽ hoạt động, vì các phiên bản có mã gỡ lỗi, thậm chí bị vô hiệu hóa, quá chậm với thời gian tăng theo cấp số nhân. Tôi đã quyết định chơi golf bằng mọi cách, và rất vui khi thấy nó giảm xuống dưới 2 giây cho 50 điểm trên máy của tôi. Nó có thể tính toán khoảng 130 điểm trong 1 phút.

Thuật toán tương tự như quét Graham , ngoại trừ việc nó phải tìm kiếm các vỏ lồi rỗng ở khắp mọi nơi.

Giải trình

Đây là một tổng quan cấp cao về cách thức hoạt động của thuật toán. Phần cốt lõi của thuật toán này chỉ là tìm kiếm các vòng lồi ngược chiều kim đồng hồ không kèm theo một điểm. Thủ tục là như thế này:

  1. Bắt đầu với một cặp điểm và danh sách tất cả các điểm khác.
  2. Nếu cặp điểm hiện tại đi chính xác qua bất kỳ điểm nào trong danh sách, hãy dừng lại.
  3. Lọc ra tất cả các điểm theo chiều kim đồng hồ của cặp hiện tại, vì chúng sẽ làm cho đa giác lõm.
  4. Đối với tất cả các điểm còn lại, hãy làm như sau:
    1. Nếu một đường thẳng từ điểm này đến điểm đầu tiên của chuỗi đi qua hoặc bao quanh bất kỳ điểm nào ngược chiều kim đồng hồ, hãy bỏ qua điểm này, vì bất kỳ đa giác nào cũng sẽ bao quanh điểm đó.
    2. Thêm điểm này vào chuỗi, lặp lại từ bước 1 với chuỗi hiện tại và danh sách các điểm.
  5. Nếu không còn điểm nào và chuỗi có ít nhất 3 điểm thì đây là đa giác lồi hợp lệ. Hãy nhớ diện tích lớn nhất của các đa giác này.

Ngoài ra, để tối ưu hóa, chúng tôi ghi lại cặp chuỗi ban đầu khi được kiểm tra, do đó, bất kỳ tìm kiếm nào sau khi thấy cặp này ở bất kỳ đâu trong chuỗi đều có thể ngừng tìm kiếm, vì đã tìm thấy đa giác lớn nhất với cặp này.

Thuật toán này sẽ không bao giờ tìm thấy một đa giác hai lần và tôi đã xác minh bằng thực nghiệm điều này.


2
+1, đây là một câu trả lời tuyệt vời. Bạn có thể thay thế ===!==bằng ==!=, nhưng tôi không thể chắc chắn nếu không hiểu mã của bạn ...
jrich

1
Cảm ơn! Những người cụ thể === và! == đang so sánh các đối tượng, thật không, thật đáng buồn. Nó được sử dụng để so sánh các chỉ số, nhưng (x,i)=>p.i==i(13 ký tự) dài hơn một chút so với x=>p===x(8 ký tự) ngay cả sau khi tối ưu hóa.
ricochet1k

2
Có một lời giải thích cho bạn @Lembik
ricochet1k

1
Bạn dường như đã đánh bại kỷ lục O (n ^ 3) được đề cập trong các bình luận của câu hỏi SO được liên kết!

1
Được rồi, tôi đang đến nơi mà tôi không tin rằng có thể điều này diễn ra trong ít hơn O (n ^ 3). Tôi rất mới với độ phức tạp thuật toán.
ricochet1k
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.