题目大意:
国王有一片森林,巫师需要从所有树中选出一些做成围栏把其他树围起来,
每棵树都有其对应的价值 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\n",c); printf("Cut these trees:"); for(int i=0;i<n;i++) { if(ans & 1) printf(" %d",i+1); ans >>= 1; } printf("\nExtra wood: %.2f\n\n",ansl); } return 0; }