POJ 1873 /// 状压+凸包

题目大意:

国王有一片森林,巫师需要从所有树中选出一些做成围栏把其他树围起来,

每棵树都有其对应的价值 v 和能作为围栏的长度 l

要求最小价值,若存在多种最小价值的方案则选择余下长度更少的

树木较少 状态压缩 枚举所有状态

计算当前的状态 被选中的 树的价值和长度

其他 被围起来(未被选中)的树去求凸包

计算凸包的边长(即围栏的最小长度)

判断选中的树是否能围住凸包 再更新答案

#include <cstdio>
#include <algorithm>
#include <cmath>
#define INF 0x3f3f3f3f
using namespace std;

const double eps=1e-10;
double add(double a,double b) {
    if(abs(a+b)<eps*(abs(a)+abs(b))) return 0;
    return a+b;
}
struct P {
    double x,y;
    P(){};
    P(double _x,double _y):x(_x),y(_y){};
    P operator - (P p) {
        return P(add(x,-p.x),add(y,-p.y)); }
    P operator + (P p) {
        return P(add(x,p.x),add(y,p.y)); }
    P operator * (double d) {
        return P(x*d,y*d); }
    double dot(P p) {
        return add(x*p.x,y*p.y); }
    double det(P p) {
        return add(x*p.y,-y*p.x); }
}p1[20], p2[20], p3[20];
int n;
double v[20], l[20], ansv, ansl;
bool cmp(P a,P b) {
    if(a.x==b.x) return a.y<b.y;
    return a.x<b.x;
}
double length(P a,P b) {
    return sqrt((a-b).dot(a-b));
}
double andrew(int n)
{
    sort(p2,p2+n,cmp);
    int k=0;
    for(int i=0;i<n;i++) {
        while(k>1 && (p3[k-1]-p3[k-2]).det(p2[i]-p3[k-1])<=0)
            k--;
        p3[k++]=p2[i];
    }
    for(int i=n-2,t=k;i>=0;i--) {
        while(k>t && (p3[k-1]-p3[k-2]).det(p2[i]-p3[k-1])<=0)
            k--;
        p3[k++]=p2[i];
    } // 凸包上的点存入p3

    double res=0;
    for(int i=1;i<k;i++) // 计算凸包边长 即围栏长度
        res+=length(p3[i],p3[i-1]); 
    return res;
}
bool solve(int task)
{
    int cnt=0, t=task;
    double cntv=0, cntl=0;
    for(int i=0;i<n;i++) {
        if(task&1) cntv+=v[i], cntl+=l[i]; // 被选中的 计算价值及长度
        else p2[cnt++]=p1[i]; // 为被选中的存入p2 待求凸包
        task >>= 1;
    }
    if(cntv>ansv) return 0; // 价值大于已有答案价值 返回0
    double tblen=andrew(cnt); // 求凸包边长 即围栏所需长度
    if(tblen>cntl) return 0; // 若长度不足于围成围栏 返回0

    if(cntv==ansv) ansl=min(ansl,cntl-tblen); // 更新余下长度的答案
    else ansv=cntv, ansl=cntl-tblen; // 存在更小的价值 更新价值和长度
    return 1; // 更新了答案 返回1 记录新的状态
}

int main()
{
    int c=0;
    while(~scanf("%d",&n)) {
        if(n==0) break; c++;
        for(int i=0;i<n;i++) {
            scanf("%lf%lf",&p1[i].x,&p1[i].y);
            scanf("%lf%lf",&v[i],&l[i]);
        }

        int ans;
        ansv=ansl=INF;
        int N=(1<<n)-1;
        for(int i=1;i<N;i++) // 枚举所有状态
            if(solve(i)) ans=i;

        printf("Forest %d
",c);
        printf("Cut these trees:");
        for(int i=0;i<n;i++) {
            if(ans & 1) printf(" %d",i+1);
            ans >>= 1;
        }
        printf("
Extra wood: %.2f

",ansl);
    }

    return 0;
}
View Code
原文地址:https://www.cnblogs.com/zquzjx/p/9649396.html