题目来源:

http://poj.org/problem?id=1873

题意:给出n棵树的坐标,树的高度和树的价值,从这些树中砍掉一些(整棵整棵的)做围栏把剩余的树围起来,使得消耗的树的价值最小。输出应砍掉哪里些树以及剩余的材料的长度。(如果砍掉的价值相同,则取砍掉数目少的)(2 <= n <= 15)。

1:因为只有15棵树,用位运算枚举砍树情况就行了。

2:枚举时注意剪枝 

3:当剩下的点集处理凸包时m<=1 时特殊处理下

代码如下:

#include <iostream>
#include <algorithm>
#include <stdlib.h>
#include <iostream>
#include <stdio.h>
#include <stack>
#include <string>
#include <string.h>
#include <algorithm>
#include <stdlib.h>
#include <vector>
#include <set>
#include <math.h>
#include <cmath>
#include <map>
#include <queue>
using namespace std ;
typedef long long LL;
const int inf=0x7fffffff;
const int N = 20;
double EPS= 1e-10;
double add(double a, double b)
{
    if( fabs(a+b)< EPS * ( fabs(a)+fabs(b) ) )  return 0;
    return a+b;
}
struct Point{
    double x,y;
    int v,l;
    double dist(Point p){
        return sqrt( add( ( x-p.x)*(x-p.x) , (y-p.y)*(y-p.y) ) );
    }
};
// pop1*p0p2 > 0 左转
double xmult(Point p1, Point p2, Point p0){
    return add( (p1.x-p0.x)*(p2.y-p0.y), -(p1.y-p0.y)*(p2.x-p0.x) );
}
Point List[N]; // 点集
int top;  // 栈顶指针
Point qs[N];
Point p[N];
bool operator<(Point a, Point b) // 按y轴值从小到大, y轴值相同时,x轴从小到大排列
{
    if(a.y != b.y) return a.y< b.y;
    else  return a.x < b.x;
}
// 这种基于平面扫描法的graham扫描算法中, qs中存储的是起点然后回到起点qs[0]=qs[top]=起点
double graham(int n)
{
    sort(List,List+n);
    double sum=0;
    if(n==0 || n==1) return 0;  // 特殊处理下
    qs[0]=List[0];
    qs[1]=List[1];
    top=1;
    //构造凸包的下侧
    for(int i=2; i<n;i++){
        while(top>=1 && xmult( qs[top],List[i],qs[top-1] )<=0 )
            top--;
        qs[++top]=List[i];
    }
    //构造凸包的上侧
    qs[++top]=List[n-2];
    int len=top;
    for(int i=n-3 ;i>=0 ; i--){
        while(top>=len &&  xmult( qs[top],List[i],qs[top-1] )<=0 )
            top--;
        qs[++top]=List[i];
    }
    for(int i=0 ; i<top ; i++)
        sum+=qs[i].dist(qs[i+1]);
    return sum;
}
// 二进制枚举
void solve(int n,int &min_val, int &cut_num, double &re_len, int &ans)
{
    for(int bit=0; bit<(1<<n); bit++) // 二进制枚举
    {
        int t=0,cut_val=0;
        double cut_len=0 ;
        for(int i=0; i<n; i++){
            if(bit & (1<<i))
            {
                cut_len+=p[i].l;
                cut_val+=p[i].v;
            }
            else
            {
                List[t].x=p[i].x;
                List[t++].y=p[i].y;
            }
        }
        if(cut_val > min_val) continue; // 一个重要的剪枝
        double c=graham(t);
        if(cut_len >=c ) // 满足长度限制,也算个剪枝吧
        {
            if(cut_val < min_val || (cut_val== min_val &&  n-t < cut_num))
            {
                min_val=cut_val;
                cut_num=n-t;
                ans=bit;
                re_len=cut_len-c;
            }
        }
    }
}
int main()
{
    int n,k=1;
    int flag=1;
    while(scanf("%d",&n),n)
    {
        if(!flag) {puts("");}
        flag=0;
        for(int i=0; i<n; i++)
            scanf("%lf%lf%d%d",&p[i].x,&p[i].y, &p[i].v, &p[i].l);
        int min_val=inf;
        int cut_num=inf;
        double re_len=0;
        int ans=0;
        solve(n,min_val,cut_num,re_len,ans);
        printf("Forest %d\n",k++);
        printf("Cut these trees:");
        for(int i=0 ; i<n; i++){
            if(ans & (1<<i))
                printf(" %d",i+1);
        }
        printf("\nExtra wood: %.2lf\n",re_len);
    }
    return 0;
}