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 точек, это допустимый выпуклый многоугольник. Запомните наибольшую площадь этих полигонов.
Кроме того, в качестве оптимизации мы записываем начальную пару цепочки как проверенную, поэтому любые поиски впоследствии при обнаружении этой пары в любом месте цепочки могут немедленно прекратить поиск, поскольку самый большой полигон с этой парой уже найден.
Этот алгоритм никогда не должен находить многоугольник дважды, и я экспериментально подтвердил это.