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 з поточним ланцюжком та списком точок.
- Якщо не залишилося точок, а ланцюг має щонайменше 3 точки, це дійсний опуклий багатокутник. Згадайте найбільшу площу цих багатокутників.
Також в якості оптимізації ми записуємо початкову пару ланцюга як перевірену, тому будь-які пошуки після цього, побачивши цю пару в будь-якій ланцюжку, можуть негайно припинити пошук, оскільки найбільший багатокутник із цією парою вже знайдений.
Цей алгоритм ніколи не повинен знаходити багатокутник двічі, і я це експериментально перевірив.