poj 1873 The Fortified Forest (二进制枚举+凸包)

版权声明:转载注明下出处就行了。 https://blog.csdn.net/LJD201724114126/article/details/84038337

题目链接:poj 1873

题意:给出n颗数,每棵树有四个值  xi, yi, vi, li ,分别表示坐标xi,yi,值vi,长度li,现在让你从中选出一些树,将剩余的树围起来,保证选出来的树的总和值要尽量小,假如有多种值,取较少数目的树。

输出:被选出来作为围墙的树,以及建造围墙剩余的长度。

参考链接:https://www.cnblogs.com/liuxin13/p/4908324.html

题解;因为题目数据不大,可以直接二进制枚举,那么剩余的树自然作为被围的树,求这些剩余的树的凸包,就行了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>

using namespace std;

#define INF 0x3f3f3f3f
const int maxn=20;

struct point{
    double x,y;
    point(){}
    point(double _x,double _y){
        x=_x;y=_y;
    }

}p[maxn],ch[maxn],data[maxn];

point operator + (point a,point b) { return point(a.x+b.x,a.y+b.y);}
point operator - (point a,point b) { return point(a.x-b.x,a.y-b.y);}
point operator * (point a,double p) { return point(a.x*p,a.y*p);}
point operator / (point a,double p) { return point(a.x/p,a.y/p);}

bool operator < (const point &a,const point &b){
    return a.x<b.x||(a.x==b.x&&a.y<b.y);
}

const double esp=1e-8;
int dcmp(double x)
{
    if(fabs(x)<esp) return 0;
    else return x<0?-1:1;
}

bool operator == (const point &a,const point &b){
    return dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0;
}

double Cross(point a,point b) { return a.x*b.y-a.y*b.x;}
double Length(point a){ return sqrt(a.x*a.x*1.0+a.y*a.y*1.0);}

bool cmp(point a,point b){
    return (a.y<b.y||(a.y==b.y&&a.x<b.x));
}

int tot;
int val,treenum; ///价值,被选树的数量
double lastlen; ///剩余的长度
int vi[maxn],li[maxn]; ///存储每棵树的价值和长度

void andrew(int n) ///凸包模板
{
    sort(p,p+n,cmp);

    tot=-1;

    if(n==2){
        ch[0]=p[0];
        ch[1]=p[1];tot=1;
        return;
    }
    if(n==1){
        ch[0]=p[0];tot=0;
        return;
    }

    for(int i=0;i<n;i++)
    {
        while(tot>0&&Cross(ch[tot]-ch[tot-1],p[i]-ch[tot-1])<=0)
            tot--;
        ch[++tot]=p[i];
    }

    for(int i=n-2,k=tot;i>=0;i--){
        while(tot>k&&Cross(ch[tot]-ch[tot-1],p[i]-ch[tot-1])<=0)
            tot--;
        ch[++tot]=p[i];
    }
}

bool solve(int bitnum,int n)
{
    int cut_val=0,cnt=0; ///选出来被砍的树的总价值,剩余树的数目
    double cut_len=0; ///选出来被砍的树的总长度

    for(int i=0;i<n;i++)
    {
        if(bitnum&1){
            cut_val+=vi[i];
            cut_len+=li[i];

        }
        else p[cnt++]=data[i]; ///没被选中,存进p结构体,等下去凸包
        bitnum>>=1;
    }

    andrew(cnt); ///求凸包

        double len=0; ///计算凸包长度
        for(int i=0;i<tot;i++){
                len+=Length(ch[i+1]-ch[i]);
            }
            len+=Length(ch[tot]-ch[0]);


    if(dcmp(cut_len-len>=0&&(val>cut_val||(val==cut_val&&treenum>n-cnt))))
    {
        lastlen=cut_len-len;
        val=cut_val;
        treenum=n-cnt;
        return 1;
    }
    return 0;

}

int main()
{

    int n,T=0;

    while(scanf("%d",&n)&&n)
    {
        val=treenum=INF;

        for(int i=0;i<n;i++)
            scanf("%lf%lf%d%d",&data[i].x,&data[i].y,&vi[i],&li[i]);


        int item=(1<<n)-1; ///二进制枚举
        int ans;

        for(int i=1;i<item;i++) ///因为不可能全选,故<
        {
            if(solve(i,n)) ans=i;
        }

        if(T) puts("");
        printf("Forest %d\n", ++T);
        printf("Cut these trees:");
        for(int i=1; ans; i++)
        {
            if(ans & 1)
                printf(" %d", i);
            ans >>= 1;
        }
        printf("\nExtra wood: %.2f\n", lastlen);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/LJD201724114126/article/details/84038337