[计算几何] 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;
}

 

posted on 2008-09-08 23:16  woodfish  阅读(999)  评论(0编辑  收藏  举报

导航