The Fortified Forest POJ - 1873 (1999 WF,凸包+二进制枚举)

题目链接:https://vjudge.net/problem/POJ-1873

题意:传说中WF水题。。。。给你树的每个坐标,价值以及建造长度,要求你砍掉一些树来把其他数都围起来,来求砍掉哪些树和多余的砍掉的木材长度,达到最优价值最少

思路:用二进制枚举所有砍树的情况(2^n),然后算出没被砍的树的凸包和周长,当砍掉树的建造周长大于等于凸包周长时,记录价值,当下一次价值大于该价值时可以直接跳过不用考虑,每次符合条件更新即可

  1 // 
  2 // Created by HJYL on 2020/2/1.
  3 //
  4 #include<iostream>
  5 #include<cstring>
  6 #include<cstdio>
  7 #include<cmath>
  8 #include<algorithm>
  9 using namespace std;
 10 const double eps=1e-8;
 11 const int maxn=100;
 12 const int INF=0x3ffff;
 13 int dcmp(double x)
 14 {
 15     if(fabs(x)<eps)
 16         return 0;
 17     return x<0?-1:1;
 18 }
 19 struct Point {
 20     double x, y;
 21     int value;
 22     int len;
 23 
 24     Point(double x = 0, double y = 0) : x(x), y(y) {}
 25 
 26     Point operator-(Point const &B) const {
 27         return Point(x - B.x, y - B.y);
 28     }
 29     bool operator<(Point const &C)const{
 30         if(x==C.x)
 31             return y<C.y;
 32         return x<C.x;
 33     }
 34 }initial,p[maxn];
 35 
 36 double Cross(Point A,Point B)
 37 {
 38     return (A.x*B.y)-(A.y*B.x);
 39 }
 40 
 41 double Len(Point A,Point B)
 42 {
 43     return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));
 44 }
 45 //极角排序
 46 bool cmp(const Point& A,const Point& B)
 47 {
 48     double t=Cross(A-initial,B-initial);
 49     if(t>0)//叉积大于0,B在A的左边,A在外凸包上
 50         return true;
 51     else if(t<0)
 52         return false;
 53     else
 54         return Len(A,initial)<Len(B,initial);//极角相同采用最近的点
 55 }
 56 int Hull(Point *p,int n,Point *ch) ///**求凸包*/
 57 {
 58     sort(p,p+n);//n顶点数
 59     int m = 0;
 60     for(int i = 0; i < n; i++)
 61     {
 62         while(m > 1 && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
 63         ch[m++] = p[i];
 64     }
 65     int k = m;
 66     for(int i = n-2; i >= 0; i--)
 67     {
 68         while(m > k && Cross(ch[m-1]-ch[m-2], p[i]-ch[m-2]) <= 0) m--;
 69         ch[m++] = p[i];
 70     }
 71     if(n > 1) m--;
 72     return m;
 73 }
 74 
 75 int main()
 76 {
 77     int n;
 78     int cas=1;
 79     while(~scanf("%d",&n)&&n)
 80     {
 81         for(int i=0;i<n;i++)
 82             scanf("%lf%lf%d%d",&p[i].x,&p[i].y,&p[i].value,&p[i].len);
 83         int minv=INF;//最优花费价值价值
 84         int cut=INF;//砍掉树的最优数目
 85         double morel=0;//剩余木材长度
 86         int ops=0;//选择的所有二进制中第几个
 87         for(int i=0;i<(1<<n);i++)//二进制枚举所有情况
 88         {
 89             double ll=0;//选择树的长度和
 90             double vv=0;//选择树的价值和
 91             int pos=0;//没被选的树的数量
 92             Point bb[maxn],ch[maxn*2];//bb记录没被选的,ch用来求凸包顶点集存坐标
 93             int cut1;//该种二进制方法下砍掉树的数量
 94             for(int j=0;j<n;j++)//一种二进制中可选与不可选
 95             {
 96                 if(i&(1<<j))//砍掉该树
 97                 {
 98                     ll+=p[j].len;
 99                     vv+=p[j].value;
100                 } else//不砍的树
101                 {
102                     bb[pos].x=p[j].x;
103                     bb[pos++].y=p[j].y;//存不砍树的点集接下来求凸包
104                 }
105             }
106             // printf("pos=%d\n",pos);
107             if(vv>minv)//这种方法利用的价值大于之前的不可取
108                 continue;
109             cut1=n-pos;//砍掉的数目
110             double zhou=0;
111             int mm=Hull(bb,pos,ch);//凸包顶点数
112             // printf("mm=%d\n",mm);
113             // printf("ch=\n");
114             // for(int kk=0;kk<mm;kk++)
115             //    printf("ch%d=(%lf,%lf)\n",kk,ch[kk].x,ch[kk].y);
116             for(int t=1;t<mm;t++)
117                 zhou+=Len(ch[t],ch[t-1]);
118             zhou+=Len(ch[0],ch[mm-1]);//凸包周长
119             //  printf("zhou=%.2lf\n",zhou);
120             if(ll>=zhou)
121             {
122                 if(vv<minv||(vv==minv&&cut1<cut))//价值小于之前的或者价值相等但是数目比之前的还要少则更优
123                 {
124                     minv=vv;
125                     cut=cut1;
126                     morel=ll-zhou;
127                     ops=i;
128                 }
129             }
130         }
131         //  printf("ops=%d\n",ops);
132         if(cas>1)
133             printf("\n");
134         printf("Forest %d\n",cas++);
135         printf("Cut these trees:");
136         for(int tt=0;tt<n;tt++)
137             if(ops&(1<<tt))//该种二进制中被砍的树的位置(编号)
138                 printf(" %d",tt+1);
139         printf("\n");
140         printf("Extra wood: %.2lf\n",morel);
141     }
142     return 0;
143 }

 

posted @ 2020-02-02 01:17  branna  阅读(164)  评论(0编辑  收藏  举报