题意:有许多树,每棵树有一个坐标(x,y),一定的高度l,一定的价值v。现在要求砍掉一些树,用它们围成一个篱笆,将所有剩余的树围起来。问怎么砍才能使所花价值最小。
题解:可以枚举组合。当然也可以用位运算枚举。求凸包时注意处理一下少于3点的情况。
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using std::memset;
using std::min;
#define eps 1e-8
#define INF 9999999999999.9
#define zero(x) (((x)>0?(x):-(x))<eps)
struct Point { double x, y, v, l; };
Point p[100], p1, p2;
Point remains[100], convex[100];
bool cut[100], res[100];
double minval, length;
int n;
//计算cross product (P1-P0)x(P2-P0)
double xmult(Point p1,Point p2,Point p0)
{
return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y);
}
double distance(Point p1,Point p2){
return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
// graham算法顺时针构造所有共线点的凸包(n*logn)
int graham_cp ( const void* a, const void* b )
{
double ret = xmult(*((Point*)a), *((Point*)b), p1);
if ( ! zero(ret) )
return ret > 0 ? 1 : -1;
return xmult(*(Point*)a, *(Point*)b, p2) > 0 ? 1 : -1;
}
void _graham ( int n, Point* p, int &top, Point* stk )
{
if ( n == 1 ) { stk[0] = p[0]; top = 1; return; }
if ( n == 2 ) { stk[0] = p[0]; stk[1] = p[1]; top = 2; return; }
int k = 0, i;
p1 = p2 = p[0];
for ( int i = 1; i < n; i++ ) //找到最左下角的点p1; p2用来求平均值点
{
if ( p1.y - p[i].y > eps || (zero(p1.y-p[i].y) && p1.x > p[i].x) )
p1 = p[k=i];
p2.x += p[i].x; p2.y += p[i].y;
}
p2.x /= n; p2.y /= n;
p[k] = p[0]; p[0] = p1; //将最左下角的点p1与p[0]交换
qsort(p+1,n-1,sizeof(Point),graham_cp);
stk[0] = p[0], stk[1] = p[1], stk[2] = p[2];
for ( top = i = 3; i < n; i++ )
{
while ( top > 2 && xmult(stk[top-2], p[i], stk[top-1] ) < -eps) top--;
stk[top++] = p[i];
}
}
int graham ( int n, Point* p, Point* convex, int maxsize = 1, int dir = 1 )
{
Point *temp = new Point[n];
int s, i;
_graham(n,p,s,temp);
convex[0] = temp[0]; n = 1;
if (s == 1) return 1;
if ( s== 2 ) { convex[1] = temp[1]; return 2;}
for ( i = (dir ? 1:(s-1)); dir ? (i<s) : i; i += (dir ? 1 : -1))
{
if ( maxsize || ! zero(xmult(temp[i-1], temp[i], temp[(i+1)%s])) )
convex[n++] = temp[i];
}
delete [] temp;
return n;
}
void combin ( int index, int cnt )
{
if ( cnt == 0 )
{
int i, s = 0;
double cost = 0, lcut = 0, l2;
for ( i = 0; i < n; i++ )
{
if ( cut[i] ) { lcut += p[i].l; cost += p[i].v;}
else remains[s++] = p[i];
}
if ( cost > minval ) return;
s = graham ( s, remains, convex, 0, 1 );
l2 = distance(convex[0],convex[s-1]);
for ( i = 1; i < s; i++ )
{
l2 += distance(convex[i],convex[i-1]);
if ( l2 > lcut ) return;
}
minval = cost;
length = lcut - l2;
for ( i = 0; i < n; i++ ) res[i] = cut[i];
return;
}
if ( index >= n ) return;
cut[index] = true;
combin ( index + 1, cnt-1 );
cut[index] = false;
combin ( index + 1, cnt );
}
int main()
{
int cs = 0;
while ( scanf("%d",&n) )
{
if ( n == 0 ) break;
for ( int i = 0; i < n; i++ )
scanf("%lf %lf %lf %lf", &p[i].x, &p[i].y, &p[i].v, &p[i].l);
minval = INF;
memset(res,0,sizeof(res));
for ( int i = 1; i < n; i++ )
{
memset(cut,0,sizeof(cut));
combin ( 0, i );
}
printf("Forest %d\n",++cs);
printf("Cut these trees: ");
for ( int i = 0; i < n; i++ )
if ( res[i] ) printf("%d ",i+1);
printf("\nExtra wood: %.2lf\n\n",length);
}
return 0;
}