poj 1873 枚举+凸包
题意:给出一些树的位置,价值,长度,现要求先砍一些树制成一定长度的篱笆将剩余的树围起来,求要砍树的最小总价值。
因为树的个数最多为15个很容易想到用二进制数表示树的状态进行遍历。
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
#define MAX_INT 123456789
#define esp 10e-8
struct point
{
int x,y;
};
point vertex[16];
int res[16],Choice[16],noChoice[16],minLeft[16];
double length[16],wight[16];
int cmp(const void * a,const void * b)
{
point p1,p2;
p1=vertex[*(int*)a]; p2=vertex[*(int *)b];
if(p1.x==p2.x)
return p1.y-p2.y;
return p1.x-p2.x;
}
int cross(point p0,point p1,point p2)
{
return (p2.y-p0.y)*(p1.x-p0.x)-(p1.y-p0.y)*(p2.x-p0.x);
}
int dist(point p1,point p2)
{
return (p1.y-p2.y)*(p1.y-p2.y)+(p1.x-p2.x)*(p1.x-p2.x);
}
int cmp1(const void* a,const void * b)
{
return *(int *)a-*(int *)b;
}
int graham(int n,int m)
{
int i,len,top=1;
if(n<2)
return 0;
qsort(Choice,n,sizeof(Choice[0]),cmp);
res[0]=Choice[0]; res[1]=Choice[1];
for(i=2;i<n;i++)
{
while(top && cross(vertex[res[top-1]],vertex[res[top]],vertex[Choice[i]])<=0)
top--;
res[++top]=Choice[i];
}
len=top; res[++top]=Choice[n-2];
for(i=n-3;i>=0;i--)
{
while(top!=len && cross(vertex[res[top-1]],vertex[res[top]],vertex[Choice[i]])<=0)
top--;
res[++top]=Choice[i];
}
return top;
}
int print(int count,int len,double leaves)
{
int i;
qsort(minLeft,len,sizeof(int),cmp1);
printf("Forest %d\n",count);
printf("Cut these trees:");
for(i=0;i<len;i++)
printf(" %d",minLeft[i]+1);
printf("\n");
printf("Extra wood: %.2lf\n\n",leaves);
return 0;
}
int main()
{
int i,j,k,n,len,len1,len2,top,count=0;
double cutLen,noCutLen,leaves,value, cur_min;
while(scanf("%d",&n) && n)
{
count++;
for(i=0;i<n;i++)
scanf("%d%d%lf%lf",&vertex[i].x,&vertex[i].y,&wight[i],&length[i]);
k=(1<<n); cur_min=MAX_INT; len=0;
for(i=k-1;i>=0;i--)
{
len1=len2=0;
memset(Choice,0,sizeof(Choice));
//选择要砍的树和不砍的树
for(j=0;j<n;j++)
{
if(i&(1<<j))
Choice[len1++]=j;
else
noChoice[len2++]=j;
}
cutLen=noCutLen=value=0;
//砍的树的总长度和总价值
for(j=0;j<len2;j++)
{
cutLen+=length[noChoice[j]];
value+=wight[noChoice[j]];
}
//计算凸包和周长
top=graham(len1,len2);
for(j=0;j<top;j++)
{
noCutLen+=sqrt((double)dist(vertex[res[j]],vertex[res[j+1]]));
}
//寻找在砍的树的总长度大于凸包周长的情况下的最小价值
if(cutLen>=noCutLen)
{
if(value<cur_min || (abs(value-cur_min)<=esp && len2<len))
{
cur_min=value; len=len2;
leaves=cutLen-noCutLen;
for(j=0;j<len2;j++)
{
minLeft[j]=noChoice[j];
}
}
}
}
print(count,len,leaves);
}
return 0;
}