[计算几何] POJ 1873 暴力+凸包

http://acm.pku.edu.cn/JudgeOnline/problem?id=1873

题目大意是从一个点集中选出一些点,是这些点的长度之和能够把剩下的点围起来,并且没个点有个权值,要求最后的选出的点的权值之和最小,如果有多个最小的,要求选出的点数最小。

由于点集数很小,n<=15,很容易想到枚举,每个点有选与不选2中状态,有2^n选点方式,没枚举一个选点方式,对剩下的点进行Graham扫描求凸包,看能不能满足要求。总的复杂度是O(2^n*n*logn)

一下有一些可以用到的优化手段:

1.枚举大可不必用dfs生成,可以直接用一个二进制数来表示选点集合。

2.在枚举出一个集合后,不要每次都求凸包,如果当前集合的权值之和>目前求出的最优权值之和,那就根本不用再往下面算了。

3.不要用vector来保存答案,这样肯定会超时。

#include  < stdio.h >
#include 
< math.h >

const   int  MAXN = 15 ;

struct  point {
    
double  x, y;
} pp[MAXN
+ 1 ],p[MAXN + 1 ],h[MAXN + 1 ];
int  v[MAXN + 1 ],l[MAXN + 1 ];
int  ans[MAXN + 1 ],an;

double  mydistance( const  point &  p1, const  point &  p2) {
    
return  sqrt( (p1.x  -  p2.x)  *  (p1.x  -  p2.x)  +  (p1.y  -  p2.y)  *  (p1.y  -  p2.y));
}

double  multiply( const  point &  sp, const  point &  ep, const  point &  op) {
    
return ((sp.x - op.x) * (ep.y - op.y) - (ep.x - op.x) * (sp.y - op.y));
}

int  partition(point a[], int  p, int  r) {
    
int  i = p,j = r + 1 ,k;
    
double  ang,dis;
    point R,S;
    k
= (p + r) / 2 ;
    R
= a[p];
    a[p]
= a[k];
    a[k]
= R;
    R
= a[p];
    dis
= mydistance(R,a[ 0 ]);
    
while ( 1 ) {
        
while ( 1 ) {
            
++ i;
            
if (i > r) {
                i
= r;
                
break ;
            }
            ang
= multiply(R,a[i],a[ 0 ]);
            
if (ang > 0 )
                
break ;
            
else   if (ang == 0 ) {
                
if (mydistance(a[i],a[ 0 ]) > dis)
                
break ;
            }
        }
        
while ( 1 ) {
             
-- j;
            
if (j < p) {
                j
= p;
                
break ;
            }
            ang
= multiply(R,a[j],a[ 0 ]);
            
if (ang < 0 )
                
break ;
            
else   if (ang == 0 ) {
                
if (mydistance(a[j],a[ 0 ]) < dis)
                    
break ;
            }
        }
        
if (i >= j) break ;
        S
= a[i];
        a[i]
= a[j];
        a[j]
= S;
    }
    a[p]
= a[j];
    a[j]
= R;
    
return  j;
}

void  anglesort(point a[], int  p, int  r) {
   
if (p < r) {
      
int  q = partition(a,p,r);
      anglesort(a,p,q
- 1 );
      anglesort(a,q
+ 1 ,r);
   }
}

// 对PointSet求凸包,点数为n,凸包上的点保存在ch中,点数位len
void  Graham_scan(point PointSet[],point ch[], int  n, int   & len) {
    
int  i,k = 0 ,top = 2 ;
    point tmp;
    
for (i = 1 ;i < n;i ++ )
        
if  ( PointSet[i].x < PointSet[k].x  ||
            (PointSet[i].x
== PointSet[k].x)  &&  (PointSet[i].y < PointSet[k].y) )
               k
= i;
    tmp
= PointSet[ 0 ];
    PointSet[
0 ] = PointSet[k];
    PointSet[k]
= tmp;
    anglesort(PointSet,
1 ,n - 1 );
    
if (n < 3 ) {
        len
= n;
        
for ( int  i = 0 ;i < n;i ++ ) ch[i] = PointSet[i];
        
return  ;
    }
    ch[
0 ] = PointSet[ 0 ];
    ch[
1 ] = PointSet[ 1 ];
    ch[
2 ] = PointSet[ 2 ];
    
for  (i = 3 ;i < n;i ++ ) {
        
while  (multiply(PointSet[i],ch[top],ch[top - 1 ]) >= 0 ) top -- ;
            ch[
++ top] = PointSet[i];
    }
    len
= top + 1 ;
}

int  main() {
    
int  n;
    
int  cases = 0 ;
    
while (scanf( " %d " , & n) != EOF && n) {
        cases
++ ;
        
for ( int  i = 0 ;i < n;i ++ )
            scanf(
" %lf %lf %d %d " , & pp[i].x, & pp[i].y, & v[i], & l[i]);
        
int  minv = 1 << 30 ;
        
double  ex = 0 ;

        
for ( int  mask = 0 ;mask < ( 1 << n);mask ++ ) {
            
int  tot = 0 ;
            
int  nowv = 0 ;
            
int  nowl = 0 ;

            
for ( int  i = 0 ;i < n;i ++ ) {
                
if (mask & ( 1 << i)) {
                    nowv
+= v[i];
                    nowl
+= l[i];
                }
else {
                    p[tot].x
= pp[i].x;
                    p[tot].y
= pp[i].y;
                    tot
++ ;
                }
            }
            
if (nowv > minv)  continue ;
            
if (tot == 0   ||  tot == n)  continue ;
            
int  len = 0 ;
            
double  nl = 0 ;
            Graham_scan(p,h,tot,len);
            
for ( int  i = 0 ;i < len;i ++ )
                nl
+= mydistance(h[i],h[(i + 1 ) % len]);
            
if (nl <= nowl) {
                
if (nowv < minv  ||  (nowv == minv && n - tot < an)) {
                    minv
= nowv;
                    an
= 0 ;
                    
for ( int  i = 0 ;i < n;i ++ if (mask & ( 1 << i)) ans[an ++ ] = i + 1 ;
                    ex
= nowl - nl;
                }
            }
        }
        
if (cases > 1 ) printf( " \n " );
        printf(
" Forest %d\n " ,cases);
        printf(
" Cut these trees: " );
        
for ( int  i = 0 ;i < an;i ++ ) printf( "  %d " ,ans[i]);
        printf(
" \n " );
        printf(
" Extra wood: %.2f\n " ,ex);
    }
    
return   0 ;
}

 

你可能感兴趣的:(poj)