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