POJ 1873 The Fortified Forest(凸包+二进制枚举)
思路:数据量少,只需要二进制枚举就得了。需要注意的是,如果都能围得上,选消耗树价值最小的。如果消耗树价值一样,选最少颗需要选上的树。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
double maxx = -1e18;
struct Point
{
double x, y;
Point(double x = 0, double y = 0):x(x),y(y) {}
};
typedef Point Vector;
Vector operator + (Vector A, Vector B)
{
return Vector(A.x+B.x, A.y+B.y);
}
Vector operator - (Point A, Point B)
{
return Vector(A.x-B.x, A.y-B.y);
}
Vector operator * (Vector A, double p)
{
return Vector(A.x*p, A.y*p);
}
Vector operator / (Vector A, double p)
{
return Vector(A.x/p, A.y/p);
}
bool operator < (const Point& a, const Point& b)
{
if(a.x == b.x)
return a.y < b.y;
return a.x < b.x;
}
const double eps = 1e-16;
int sgn(double x)
{
if(fabs(x) < eps)
return 0;
if(x < 0)
return -1;
return 1;
}
bool operator == (const Point& a, const Point& b)
{
if(sgn(a.x-b.x) == 0 && sgn(a.y-b.y) == 0)
return true;
return false;
}
double Dot(Vector A, Vector B)
{
return A.x*B.x + A.y*B.y;
}
double Length(Vector A)
{
return sqrt(Dot(A, A));
}
double Angle(Vector A, Vector B)
{
return acos(Dot(A, B)/Length(A)/Length(B));
}
double Cross(Vector A, Vector B)
{
return A.x*B.y-A.y*B.x;
}
double Area2(Point A, Point B, Point C)
{
return Cross(B-A, C-A);
}
Vector Rotate(Vector A, double rad) //rad为弧度 且为逆时针旋转的角
{
return Vector(A.x*cos(rad)-A.y*sin(rad), A.x*sin(rad)+A.y*cos(rad));
}
Vector Normal(Vector A) //向量A左转90°的单位法向量
{
double L = Length(A);
return Vector(-A.y/L, A.x/L);
}
double ToLeftTest(Point a, Point b, Point c)
{
return Cross(b - a, c - a) ;
}
struct Line
{
Point v, p;
Line() {}
Line(Point v, Point p):v(v), p(p) {}
Point point(double t)
{
return v+(p-v)*t;//返回点P = v + (p - v)*t
}
};
Point GetLineIntersection(Point P, Vector v, Point Q, Vector w)
{
Vector u = P-Q;
double t = Cross(w, u)/Cross(v, w);
return P+v*t;
}
bool LineIntersection(Point a, Point b, Point c, Point d) //直线ab与线段cd相交
{
return sgn(Cross(b - a, c - a)) *sgn( Cross(b - a, d - a)) <=0;
}
Point c;
bool cmp2(Point a,Point b)
{
if(Cross(b-a,c-a)==0)//计算叉积,函数在上面有介绍,如果叉积相等,按照X从小到大排序
return a.x<b.x;
else return Cross(b-a,c-a)>0;
}
double DistanceToLine(Point P, Point A, Point B)
{
Vector v1 = B-A, v2 = P-A;
return fabs(Cross(v1, v2)/Length(v1));
}
double ConvexHull(Point* p, int n) //改一下模板,返回凸包的周长
{
Point ch[200];
sort(p, p+n);
int m = 0;
for(int i = 0; i < n; ++i)
{
while(m > 1 && Cross(ch[m-1] - ch[m-2], p[i] - ch[m-2]) < 0)
{
m--;
}
ch[m++] = p[i];
}
int k = m;
for(int i = n-2; i>= 0; --i)
{
while(m > k && Cross(ch[m-1] - ch[m-2], p[i] - ch[m-2]) < 0)
{
m--;
}
ch[m++] = p[i];
}
if(n > 1)
--m;
double ans = 0;
if(m == 1)
ans = 0;
else
{
ch[m] = ch[0];
for(int i = 0; i<m; i++)
{
ans+= sqrt((ch[i].x-ch[i+1].x)*(ch[i].x-ch[i+1].x)+(ch[i].y-ch[i+1].y)*(ch[i].y-ch[i+1].y));
}
}
return ans;
}
int main()
{
int Case = 0;
int n;
double x[20], y[20],v[20],h[20];
while(scanf("%d", &n)!=EOF && n)
{
if(Case)printf("\n");
for(int i = 0; i<n; i++)
{
scanf("%lf%lf%lf%lf",&x[i],&y[i],&v[i],&h[i]);
}
int fnal_cnt = 1e5;
double fnal_val = 1e5;
double fnal_len = 0;
int sum = 0;
for(int i = 0; i<(1<<n); i++)
{
double temp_val = 0;
double temp_len = 0;
Point p[100];
int cnt = 0;
for(int j = 0; j<n; j++)
{
if((i&(1<<j)))
{
p[cnt++] = Point(x[j],y[j]);
}
else//要砍掉的树
{
temp_val +=v[j];
temp_len +=h[j];
}
}
double tubao_len = ConvexHull(p,cnt);
if(sgn(temp_len-tubao_len)>0)//能围上
{
if(sgn(fnal_val-temp_val) >0 ||(sgn(fnal_val-temp_val) == 0&&sgn( cnt-fnal_cnt)<0) )
{
fnal_cnt = cnt;
fnal_val = temp_val;
fnal_len = temp_len-tubao_len;
sum = i;
}
}
}
printf("Forest %d\nCut these trees:",++Case);
for(int i =0; i<n; i++)
{
if(!(sum &(1<<i)))
printf(" %d",i+1);
}
printf("\nExtra wood: %.2f\n",fnal_len);
}
return 0;
}