poj 1873 The Fortified Forest

题意:n棵树 xi, yi, vi, li  

给出每棵树的位置( xi, yi ),价值vi,  砍了这棵树能造多长 的围墙 li   


n最大只有15,所以直接采用枚举

满足条件 砍了的树足够将剩下的数围起来(凸包),输出 剩下的树的价值和最大的方案。

存在多种方案时,输出,剩下的树最少的方案

Forest 1
Cut these trees: 砍了哪些树
Extra wood: 砍了的树的li之和 - 剩下的数围成的凸包之长

注意 

数据用double类型

只剩两颗树   围墙的长度 = 2 * 两棵树的距离

只剩一颗树    不需要围


 

#include <cstdio>
#include <cstring>
#include <cmath>
#include <string>
#include <algorithm>
using namespace std;

struct node
{
    double x,y;
    double v;
    double l;
    int num;//编号第几棵树
};

struct repy
{
    int status;//二进制上的1表示有这课树
    int left;//剩下树的数目
    double cost;//花费的价值
    double extra;//围完后剩下的木头
};

node tree[20];
repy ret;
int n;

bool cmp(const node &a, const node &b)
{
    if(a.y == b.y)
        return a.x < b.x;
    return a.y < b.y;
}

double mydis(int i, int j)
{
    double a = tree[i].x - tree[j].x;
    double b = tree[i].y - tree[j].y;
    return sqrt(a*a + b*b);
}

double mult(int s,int e, int o)
{
    return (tree[s].x-tree[o].x)*(tree[e].y-tree[o].y) - (tree[e].x-tree[o].x)*(tree[s].y-tree[o].y);
}

void check(int s)
{
    ret.status = s;
    ret.left = 0;
    ret.cost = 0;
    ret.extra = 0;
    int p[20],tn = 0;
    int res[20],top = 1;

    for(int i = 0; i < n; i++)
    {
        p[i] = 0;
        if(s & (1<<i))
        {
            ret.left++;
            ret.cost += tree[i].v;
            p[tn++] = i;
        }
        else ret.extra += tree[i].l;
    }

    if(ret.left == 1)
        return;

    if(ret.left == 2)
    {
        ret.extra -= 2*mydis(p[0],p[1]);
        return;
    }


    for(int i = 0; i < 3; i++)
        res[i] = p[i];
    for(int i = 2; i < tn; i++)
    {
        while(top && mult(p[i],res[top],res[top-1]) >= 0)
            top--;
        res[++top] = p[i];
    }
    int len = top;
    res[++top] = p[tn-2];
    for(int i = tn-3; i >= 0; i--)
    {
        while(top != len && mult(p[i],res[top],res[top-1]) >= 0)
            top--;
        res[++top] = p[i];
    }

    double ans = 0;
    for(int i = 0; i < top-1; i++)
    {
        ans += mydis(res[i],res[i+1]);
    }
    ans += mydis(res[0],res[top-1]);

    ret.extra -= ans;

    return ;
}

int main(int argc, char const *argv[])
{
    int end,cnt = 0;
    int num[20];
    repy ans;

    while(~scanf("%d", &n) , n)
    {
        ans.cost = 0;
        ans.left = 1e5;
        for(int i = 0; i < n; i++)
        {
            scanf("%lf %lf %lf %lf", &tree[i].x, &tree[i].y, &tree[i].v, &tree[i].l);
            tree[i].num = i+1;
        }
        sort(tree,tree+n,cmp);

        end = 1<<n;

        for(int s = 1; s < end; s++)
        {
            check(s);
            if(ret.extra > -0.00000001)
            {
                if(ret.cost > ans.cost)
                    ans = ret;
                else if(ret.cost==ans.cost && ret.left < ans.left)
                        ans = ret;
            }
        }
        printf("Forest %d\n", ++cnt);
        int cou = 0;
        for(int i = 0; i < n; i++)
        {
            if(! (ans.status & (1<<i)))
                num[cou++] = tree[i].num;
        }
        sort(num,num+cou);
        printf("Cut these trees:");
        for(int i = 0; i < cou; i++)
            printf(" %d", num[i]);
        printf("\nExtra wood: %.2lf\n\n", ans.extra);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值