TAKMIČENJA IZ PROGRAMIRANJA


Sva pitanja, predloge ili primedbe u vezi sa takmičenjima iz programiranja možete slati na e-mail:

tak.prog@gmail.com

U toku perioda za žalbe, sve žalbe možete slati na ovaj isti e-mail.

farmeri.cpp    3,558 b      izvorni kod rešenja
farmeri.tests.rar    146,188 b      test primeri
farmeri.checker.cpp    501 b      izvorni kod programa za testiranje

zadatak: Gramzivi farmeri

Poslednjih nekoliko meseci, naš stari farmer Branko, videvši da se dosta farmera preselilo u grad, odlučio je da podeli svoju zemlju ostalim farmerima koji će vredno da rade na njoj, i time spreči da njegovo mestašce nestane.
Farmer Gojko, koji je živeo nedaleko od Brankovog poseda, je upravo saznao za ovu akciju. Požurio je i on da dobije neko parče zemlje, ali već je bilo kasno. Branko je već podelio skoro celu zemlju. Ostalo je samo jedno malo parče zemlje.
Iznenađen ovakvim razvojem situacije, farmer Branko je izmislio igru, kojom će podeliti ostatak svoje zemlje, tako da što više ljudi dobije svoje parče. Branko je postavio n (n ≤ 2000) stubića u zemlju. Onaj ko prvi dođe ima pravo da uzme bilo koje četvorougaono parče zemlje, čija su temena tamo gde se nalaze stubići. Pravila igre za one koji stignu kasnije su veoma komplikovana. Na vašu sreću, farmer Gojko je stigao prvi, i sada treba da izabere četiri stubića. Pomozite mu da izabere najbolja 4 stubića, tako da dobije zemlju najveće površine.

Ulaz.

(Ulazni podaci se učitavaju iz datoteke farmeri.in.) U prvoj redu ulaza nalazi se broj n (4 ≤ n ≤ 2.000). U sledećih n redova nalaze se po dva mešovita (realna) broja - x i y koordinata jednog stubića (-200.000,0 ≤ x, y ≤ 200.000,0). U 50% test primera, n ≤ 200.

Izlaz.

(Izlazni podaci se ispisuju u datoteku farmeri.out.) U izlaznom fajlu trebate ispisati najveću površinu koju farmer Gojko može dobiti, zaokruženu na tri decimale.

Napomena. Nisu svi stubići kolinearni, tj rešenje će uvek biti veće od 0.

Primer 1.

farmeri.in      farmeri.out
6
0 0
0 1
1 0
1 1
0.5 0.5
1.5 1.5
        
1.500
rešenje

Primetimo prvo da svaki četvorougao (i konveksni i ne konveksni) sadrži bar jednu svoju dijagonalu. Za neki par tačaka A i B, izračunaćemo tačke C i D, tako da je C sa leve, a D sa desne strane prave AB, i da trouglovi ABC i ABD imaju najveću moguću površinu. Tako da znamo, da od svih četvorouglova koji sadrže dijagonalu AB u sebi, najveća moguća površina je baš P(ACBD). Ako izračunamo P(ACBD) za svaki par tačaka A i B, najveći od tih brojeva je traženi rezultat.
Ako, za fiksirano A i B, prođemo kroz svaku tačku sa leve strane i tako izračunamo C, i slićno za D, složenost ovog rešenja je O(n3), što donosi 50 poena.
Ako primetimo da tačka C je najudaljenija od segmenta AB, znamo da ona mora biti jedna od tačaka sa konveksnog omotača. Neka su E1, E2 ... Ek tačke na konveksnom omotaču sa leve strane segmenta AB, i označimo sa Hi rastojanje tačke Ei do prave AB. Znamo da je P(ABEi) = |AB| * Hi / 2. Takođe znamo da je H1 < H2 < ... < Ht > ... > Hk, i C = Ht. Tako da možemo binarnom pretragom naći tačku Ht. Složenost ovog rešenja je O(n2 * log n), što donosi oko 80 poena.
Lako se vidi da samo jedna tačka najvećeg četvorougla ne mora biti na konveksnom omotaču (i to ako i samo ako se nalaze tačno tri tačke na konveksnom omotaču). Tako da možemo reći da to mora biti tačka A. Za svaku tačku A (i sa konvekson i one koje nisu), izračunaćemo najbolje tri tačke B, C i D na konveksnom omotaču. Neka su E1, E2 ... Ek tačke sa konveksnog omotača. Ako smo izračunali C = Ev i D = Ew, za neku tačku B = Eu, gde se mogu nalaziti tačke C i D, za B = Eu + 1? Ako povećamo v dok je Hv < Hv + 1, dobijamo baš novu tačku C = Ev, i na slični način dobijemo i D. Koja je složenost ovog algoritma. Primetimo da za jednu tačku A, i v i w mogu svaki broj proći tačno jednom, tako da je složenost O(n2). Ovo rešenje donosi 100 poena.

Očigledno rešenje u O(n4) donosi 10-20 poena.

Neko odvojeno može uraditi slučaj ako na konveksnom omotaču imaju tačno tri tačke, ali samo jedan primer ima slučaj da su tačno tri tačke na omotaču. Ali je sa ovim zapažanjem rešenje malo kraće i jednostavnije.
Ako postoje više od tri tačke na konveksnom omotaču, možemo posmatrati samo tačke sa omotača. Test primeri 5., 13. i 15. imaju malo tačaka na omotaču, pa ovo zapažanje donosi poene na ovim primerima.

Test primeri:
nom - broj na konveksnom omotaču

rbr. n nom
1.
4
3
2.
30
7
3.
50
50
4.
70
70
5.
100
14
6.
100
100
7.
130
130
8.
150
149
9.
200
166
10.
200
200
rbr. n nom
11.
300
257
12.
500
428
13.
700
232
14.
700
694
15.
2000
22
16.
2000
572
17.
1000
993
18.
1500
1480
19.
2000
816
20.
2000
1966
fajl: farmeri.cpp
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

const int maxn=2000;
const double eps=1e-8;
const double infinite=1e30;
const double pi=3.141592653589; 

typedef struct
{ double x,y,angle;        
} point;

int n,noh,hull[maxn];
point p[maxn];



inline int different(int i,int j,int k=-1,int l=-2)
{
    return ( (i!=j) && (i!=k) && (i!=l) && (j!=k) && (j!=l) && (k!=l) );  
}



inline double area(int i,int j,int k,int l)
{
    double a=0;   
    a+=p[i].x*p[j].y-p[j].x*p[i].y;
    a+=p[j].x*p[k].y-p[k].x*p[j].y;    
    a+=p[k].x*p[l].y-p[l].x*p[k].y; 
    a+=p[l].x*p[i].y-p[i].x*p[l].y; 
    if (a<0) a=-a;   
    return a;       
}

inline double area(int i,int j,int k)
{
    double a=0;   
    a+=p[i].x*p[j].y-p[j].x*p[i].y;
    a+=p[j].x*p[k].y-p[k].x*p[j].y;    
    a+=p[k].x*p[i].y-p[i].x*p[k].y; 
    return a;       
}
int compare(const void *pa,const void *pb)
{
   point *a=(point*)pa,*b=(point*)pb;

  if ((a->angle)>(b->angle)) return 1;
  if ((a->angle)==(b->angle)) return 0;
  return -1;
}

int left(int a,int b,int j)
{
  double t=(p[b].x-p[a].x)*(p[j].y-p[a].y)-(p[b].y-p[a].y)*(p[j].x-p[a].x);
  if (t<-eps) return 1;
  if (t>eps) return -1; 
  return 0;
}

int main()
{
    int ts=clock();
    
    freopen("farmeri.in","r",stdin);
    freopen("farmeri.out","w",stdout);
        
    int i,j,k,l,nextl;
    double result,inter,bestleft,bestright;
    point pom;
        
    scanf("%d",&n);
    for(i=0;i<n;i++)
       scanf("%lf%lf",&(p[i].x),&(p[i].y));
 
 //------------------------
    p[0].angle=0;
    k=0;

    for(i=1;i<n;i++)
    {
      p[i].angle=0;
      if ( (p[k].y>p[i].y) || (((p[i].y-p[k].y)<eps)&&(p[k].x<p[i].x)) )
       k=i;
    }

    pom=p[0];p[0]=p[k];p[k]=pom;

    for(i=1;i<n;i++)
    {
       p[i].x-=p[0].x;
       p[i].y-=p[0].y;
       p[i].angle=atan2(p[i].y,p[i].x);
       if (p[i].angle<0) p[i].angle+=2*pi;
    }

    p[0].x=0;p[0].y=0;p[0].angle=0;

    qsort(p,n,sizeof(point),compare);

    hull[0]=0;hull[1]=1;
    k=1;
  
  
    for(i=2;i<n;i++)
    {
       while ((k>=1) && (left(hull[k-1],hull[k],i)==1)) k--;
       k++;
       hull[k]=i;                    
    }

    while ((k>=1) && (left(hull[k-1],hull[k],hull[0])==1)) k--;

    k++;
 
    noh=k;
    
    result=-infinite;     
    if (noh>3)    
       for(i=0;i<noh;i++)
       {
          k=i+1;l=(i+3)%noh;
          for(j=i+2;j<noh;j++)
          {
             while (area(hull[i],hull[k],hull[j])<area(hull[i],hull[k+1],hull[j])) k++;
             nextl=(l+1)%noh;
             while (area(hull[i],hull[j],hull[l])<area(hull[i],hull[j],hull[nextl]))
                {l=nextl;nextl=(l+1)%noh;}                                   
             inter=area(hull[i],hull[k],hull[j])+area(hull[i],hull[j],hull[l]);
             if (inter>result) result=inter;                          
          }
       }
    else 
       for(i=0;i<n;i++)
          if (different(hull[0],hull[1],hull[2],i)) 
          {
             inter=area(hull[0],hull[1],hull[2],i);
             if (inter>result) result=inter;
             inter=area(hull[0],hull[1],i,hull[2]);
             if (inter>result) result=inter;
             inter=area(hull[0],i,hull[1],hull[2]);
             if (inter>result) result=inter;                                                                          
          }
           
    printf("%.3lf\n",result / 2);
    
    return 0;    
}