POJ 1873 The Fortified Forest

题意:有许多树,每棵树有一个坐标(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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值