Знайдіть площу найбільшого опуклого багатокутника


29

Давши список цілих координат, знайдіть область найбільшого опуклого многокутника, яку ви можете побудувати зі списку таким чином, що -

  • кожна вершина є у списку
  • жоден елемент списку не міститься в полігоні.

Приклад:

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

Візуалізовано:

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 вершин за хвилину на сучасному ПК.

Найкоротший код у байтах виграє.


Чи знаєте ви швидкий алгоритм у гіршому випадку?
xnor

3
Якщо ви хочете встановити обмеження часу на 100 вершин, вам, мабуть, слід включити принаймні один такий тестовий випадок (в ідеалі декілька, наприклад, той, де всі 100 вершин є частиною рішення, той, де є 99, і той, де лише 10). .
Мартін Ендер

@ MartinBüttner На жаль, я не можу генерувати цей тестовий випадок, оскільки сам не маю робочої реалізації. Проблема досить складна :)
orlp

@xnor Кілька прикладів можна знайти тут .
orlp

"округлюється до не менше двох цифр після десяткової коми"?
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 байт

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 хвилину.

Алгоритм схожий на сканування Грема , за винятком того, що він повинен шукати порожні опуклі корпуси скрізь.

Пояснення

Ось огляд на високому рівні того, як працює алгоритм. М'ясо цього алгоритму просто шукає опуклі петлі проти годинникової стрілки, які не закривають крапку. Процедура виглядає приблизно так:

  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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.