Magic Sticks
Jump to navigation
Jump to search
Key intuition is largest area arrangement of a perimeter is when vertices lie along a circle. Cannot directly calculate, so have to binary search search it.
- check if segments satisfy triangle inequality
- start with circle equal in diameter to longest edge
- place all points along perimeter in turn
- place the first point at 0 rad
- figure out angle needed to next point based on circle radius and segment length
- figure out if you subtended more or fewer than 2-pi rads
- if we went around too far, it means the circle will need to grow and the shape will subtend the whole circle
- if we didn't go around far enough, it means the circle will still need to grow (it has too because longest edge is diameter already!) but the shape will exist only in a sector subtended by the longest edge
- for the first case, simply binary search the circle radius until you get exactly 2-pi
- for the second case, binary search until you get the angle of the all the shorter edges to match that of the longest edge
- remove the longest edge from the group and recurse on both sides, in case this yields a higher result (which it will if your edges are borderline on the triangle inequality)
import java.util.Scanner;
public class g {
Scanner in=new Scanner(System.in);
public static void main(String[] args) {
new g().go();
}
private void go() {
int cnum=0;
while(true){
cnum++;
int n=in.nextInt();
if(n==0)break;
int[] s=new int[n];
for(int i=0;i<n;i++)s[i]=in.nextInt();
System.out.printf("Case %d: %.10f\n", cnum,area(s,0,s.length));
}
}
private double area(int[] s, int start, int length) {//find the maximum area from s[start] to s[start+length-1]
if(length<3)return 0;
int max=0;
int tot=0;
int imax=0;
for(int i=start;i<start+length;i++){//find longest edge
if(s[i]>max){
max=s[i];
imax=i;
}
tot+=s[i];
}
if(max>=tot/2D)return area(s,start,imax-start)+area(s,imax+1,length-(imax-start)-1);//check triangle ineq
double r=max/2D;//set radius such that longest edge is on a diameter
double lo=r;
double hi;
double ans=0;
if(degcnt(s,start,length,r,imax)>2*Math.PI){//if circle is too small, shape is whole circle
while(degcnt(s,start,length,r,imax)>=2*Math.PI)r*=2;//make radius big enough
hi=r;
r/=2;
while(true){//binary search
double tmp=degcnt(s,start,length,r,imax);
if(tmp<2*Math.PI){
hi=r;
r=(r-lo)/2+lo;
}
if(tmp>2*Math.PI){
lo=r;
r=(hi-r)/2+r;
}
if(Math.abs(tmp-2*Math.PI)<.000000000001)break;
}
for(int i=start;i<start+length;i++)ans+=s[i]/2D*Math.sqrt(r*r-Math.pow(s[i]/2D, 2));//calculate area
}else{//else circle too big, meaning shape is only in half the circle
while(smalldegcnt(s,start,length,r,imax)<=2*Math.PI)r*=2;//make circle big enough..should never be called
hi=r;
r/=2;
while(true){//binary search
double tmp=smalldegcnt(s,start,length,r,imax);
if(tmp<2*Math.PI){
lo=r;
r=(hi-r)/2+r;
}
if(tmp>2*Math.PI){
hi=r;
r=(r-lo)/2+lo;
}
if(Math.abs(tmp-2*Math.PI)<.000000000001)break;
}
for(int i=start;i<start+length;i++)ans+=s[i]/2D*Math.sqrt(r*r-Math.pow(s[i]/2D, 2));//calculate area
ans-=2*(s[imax]/2D*Math.sqrt(r*r-Math.pow(s[imax]/2D, 2)));
}
return Math.max(ans, area(s,start,imax-start)+area(s,imax+1,length-(imax-start)-1));//return max of this or removing longest edge
}
private double smalldegcnt(int[] s, int start, int length, double r,int imax) {//shape is on one side of circle
double ans=0;
for(int i=start;i<start+length;i++)ans+=2*Math.asin(s[i]/2D/r);
return ans+2*Math.PI-4*Math.asin(s[imax]/2D/r);
}
private double degcnt(int[] s, int start, int length, double r,int imax) {//shape covers whole circle
double ans=0;
for(int i=start;i<start+length;i++)ans+=2*Math.asin(s[i]/2D/r);
return ans;
}
}
//Magic Sticks
//geometry
//polygon area
//binary search
//dp