题目让你用最少的花费(砍掉树木的vi之和)以及砍掉树的数目最少,然后用砍掉树的li之和去围住剩余的树木。
就是一个简单的搜索 ,我都几乎木有加优化了,简单的状态拍一下序,就1Y了呀
计算几何拍了个板真好呀,刷题腰不酸背不疼越刷越带感,刷题和切菜一样(可能刷水题多了吧)
#include <cstdio>
#include <iostream>
#include <string.h>
#include <cmath>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
/*my_template*/
/*data type*/
typedef pair<int,int> pii;
typedef long long ll; typedef double dl;
typedef vector<pii> vii;
typedef set<pii> sii;
/*sim_operarion*/
#define sfint(x) scanf("%d",&x)
#define sfint2(x,y) scanf("%d%d",&x,&y)
#define sfint3(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define sfstr(c) scanf("%s",c)
#define sfdl(x) scanf("%lf",&x)
#define pfint(x) printf("%d\n",x)
#define fr(i,s,n) for(int i=s;i<n;++i)
#define _fr(i,n,s) for(int i=n-1;i>=s;--i)
#define cl(a) memset(a,0,sizeof(a))
/*bitmask*/
#define _ni(x) (1<<(x))
#define _niL(x) (1LL<<(x))
#define _has(s,x) ((s&(_ni(x)))!=0)
#define _hasL(s,x) ((s&(_niL(x)))!=0LL)
template<class T> inline T _lowbit(const int &x){ return (x^(x-1))&x; }
template<class T> inline int _bitsize(const T &x){ return (x==0)?0:(1+_bitsize(x&(x-1))); }
/*CONST VALUE*/
const double pi=acos(-1.0);
const double eps=1e-8;
/*my_template*/
/*Computational Geometry*/
const int N = 17;
struct Point{
double x,y,vi,li;
int pos;
Point(double _x=0,double _y=0){
x=_x,y=_y;
}
void read(){
scanf("%lf%lf%lf%lf",&x,&y,&vi,&li);
}
}p[N], q[N];
struct Line{
double a,b,c;//表示一条直线ax+by+c=0;
Line(double _a,double _b,double _c):a(_a),b(_b),c(_c){}
Line(){}
void read(){
scanf("%lf%lf%lf",&a,&b,&c);
}
};
struct Lineseg{
Point s,e;
Lineseg(){}
Lineseg(Point _s ,Point _e ){
s = _s; e = _e;
}
void read(){
s.read();e.read();
}
};
class CG2d{
public:
bool equal_double(double a,double b){
return (fabs(a-b)<eps);
}
double dist_2(Point p1,Point p2){
return ((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
double dist(Point p1,Point p2){
return sqrt(dist_2(p1,p2));
}
bool equal_point(Point p1,Point p2){
return equal_double(p1.x,p2.x)&&equal_double(p1.y,p2.y);
}
/*Cross product ;>0 ep在矢量opsp的逆时针方向 ;=0 共线*/
double multiply(Point op,Point sp ,Point ep){
return ((sp.x-op.x)*(ep.y-op.y)-(ep.x-op.x)*(sp.y-op.y));
}
/*Dot product ;r<0 夹角为锐角; =0 直角 ;>0 钝角*/
double dotmultiply(Point op ,Point sp, Point ep){
return ((sp.x - op.x)*(ep.x-op.x)+(sp.y-op.y)*(ep.y-op.y));
}
bool onlineseg(Lineseg l,Point p){
return ((multiply(l.s,l.e,p)==0)&&( ((p.x-l.s.x)*(p.x-l.e.x)<=0) && ((p.y-l.s.y)*(p.y-l.e.y)<=0) ) );
}
/*返回点p以点o为圆心逆时针旋转alpha(单位:弧度)后所在的位置*/
Point rotate(Point o ,double alpha,Point p){
Point ret;
p.x -= o.x;p.y -= o.y;
ret.x = p.x*cos(alpha) - p.y*sin(alpha) + o.x;
ret.y = p.y*cos(alpha) + p.x*sin(alpha) + o.y;
return ret;
}
/*angle计算夹角*/
double angle(Point op,Point sp,Point ep){
double cosfi,phi,norm;
double dsx = sp.x - op.x,dsy = sp.y - op.y;
double dex = ep.x - op.x,dey = ep.y - op.y;
cosfi = dsx*dex + dsy * dey;
norm = (dsx*dsx+dsy*dsy)*(dex*dex+dey*dey);
cosfi /= sqrt(norm);
if (cosfi >= 1.0) return 0;
if (cosfi <= -1.0) return -pi;
phi = acos(cosfi);
if (dsx*dey-dsy*dex>0) return phi;
return -phi;
}
/*******************线段与直线的基本运算***************/
/*判断点与线段的关系,用途很广泛*/
double relation(Point p,Lineseg l){
return dotmultiply(l.s,p,l.e)/dist_2(l.s,l.e);
}
/*垂足点p到线段l的垂足*/
Point perpendicular(Point p,Lineseg l){
double r = relation(p,l);
Point tp(l.s.x + r * (l.e.x - l.s.x),l.s.y + r * (l.e.y - l.s.y));
return tp;
}
/*求点P到线段l的最短距离,并返回线段上距该点最近的点np*/
double ptolinesegdist(Point p,Lineseg l,Point &np){
double r = relation(p,l);
if (r<0) np = l.s;
else if (r>1) np = l.e ;
else np = perpendicular(p,l);
return dist(p,np);
}
/*求点p到线段l所在直线的距离*/
double ptolinedist(Point p,Lineseg l){
return fabs(multiply(l.s,l.e,p))/dist(l.s,l.e);
}
/*求点到折线集的最近距离,并返回最近点*/
double ptopointset(int vcnt,Point pointset[],Point p,Point &q){
double rd = 1000000000.0,td; //rd初始化 赋值成一个很大的值
Lineseg l;
Point rq,tq;
for(int i = 0;i < vcnt-1; ++i){
l.s = pointset[i];l.e = pointset[i+1];
td = ptolinesegdist(p,l,tq);
if (td < rd) rd = td,rq = tq;
}
q = rq;
return rd;
}
double ptopolygon(int vcnt,Point pointset[],Point p,Point &q){
Point tq;
double d = ptopointset(vcnt,pointset,p,q);
double td = ptolinesegdist(p,Lineseg(pointset[vcnt-1],pointset[0]),tq);
if (td < d) {
q = tq;
return td;
}
return d;
}
/*判断圆是否在多边形内 这里木有判断圆心是否在多边形内,默认在里面*/
bool CircleInsidePolygon(int vcnt,Point polygon[],Point center,double radius){
Point q;
double d = ptopolygon(vcnt,polygon,center,q);
if (radius <= d || fabs(d-radius) < eps) return 1;
return 0;
}
/*返回 两个矢量l1和l2的夹角的余弦 (-1..1) 注意如果要求余弦的话,反余弦函数的定义是从0到pi的*/
double cosine(Lineseg l1,Lineseg l2){
return ((l1.e.x-l1.s.x) * (l2.e.x - l2.s.x) + (l1.e.y - l1.s.y) * (l2.e.y - l2.s.y))
/(dist(l1.s,l1.e)*dist(l2.s,l2.e));
}
/*返回线段l1 与 l2之间的夹角, 单位为弧度 范围(-pi,pi)*/
double lsangle(Lineseg l1,Lineseg l2){
Point o(0,0),s,e;
s.x = l1.e.x - l1.s.x;
s.y = l1.e.y - l1.s.y;
e.x = l2.e.x - l2.s.x;
e.y = l2.e.y - l2.s.y;
return angle(o,s,e);
}
/*如果线段u和v 相交(包括相交在端点处)时,然后true*/
bool Isintersect(Point a1, Point a2, Point b1, Point b2) {
//判断两条线段是否相交
return min(a1.x, a2.x) <= max(b1.x, b2.x)&&
min(a1.y, a2.y) <= max(b1.y, b2.y) &&
min(b1.x, b2.x) <= max(a1.x, a2.x) &&
min(b1.y, b2.y) <= max(a1.y, a2.y) &&
multiply(a1, a2, b1) * multiply(a1, a2, b2) <= 0 &&
multiply(b1, b2, a1) * multiply(b1, b2, a2) <= 0 ;
}
/*如果线段u和v 相交(不包括相交在端点处)时,然后true*/
bool IsintersectNotOnline(Point a1, Point a2, Point b1, Point b2){
return Isintersect(a1,a2,b1,b2) && (!onlineseg(Lineseg(b1,b2),a1)) &&
(!onlineseg(Lineseg(b1,b2),a2)) && (!onlineseg(Lineseg(a1,a2),b1)) &&
(!onlineseg(Lineseg(a1,a2),b2));
}
/*直线与线段相交模板:点(P1,P2)是否与过点(Q1,Q2)的直线相交 没有判规范 */
bool intersect_line_seg(Point P1,Point P2,Point Q1,Point Q2) {
return multiply(Q1,P1,Q2)*multiply(Q1,Q2,P2)>=0;
}
//已知直线过的两点 求过这两点的直线方程 ax+by+c=0; a>=0;
Line makeline(Point p1,Point p2){
Line tl;
tl.a = p2.y - p1.y;
tl.b = p1.x - p2.x;
tl.c = p1.y * p2.x - p1.x * p2.y;
//if (tl.a<0) tl.a = -tl.a,tl.b=-tl.b,tl.c=-tl.c;
return tl;
}
//计算直线的倾斜角
double slope(Line l){
if (fabs(l.a) < eps) return 0.0;
if (fabs(l.b) < eps) return 1e20; //斜率无穷大
return -(l.a/l.b);
}
//返回直线的倾斜角
double alpha(Line l){
if (fabs(l.a)<eps) return 0.0;
if (fabs(l.b)<eps) return pi/2;
double k = slope(l);
if (k>0) return atan(k);
return pi+atan(k);
}
//求点p关于直线l 的对称点
Point symmetry(Line l ,Point p){
Point rp;
rp.x = ((l.b*l.b-l.a*l.a) * p.x - 2*l.a*l.b*p.y - 2*l.a*l.c)/(l.a*l.a+l.b*l.b);
rp.y = ((l.a*l.a-l.b*l.b) * p.y - 2*l.a*l.b*p.x - 2*l.b*l.c)/(l.a*l.a+l.b*l.b);
return rp;
}
/*如果两条直线l1 和 l2 相交返回true 和交点p*/
bool lineintersect(Line l1,Line l2,Point &p){
double d = l1.a * l2.b - l2.a*l1.b;
if (fabs(d)<eps) return 0;
p.y = (l1.c * l2.a - l1.a * l2.c)/d;
p.x = (l2.c * l1.b - l1.c * l2.b)/d;
return 1;
}
//判断直线重合
bool Line_Coincide(const Line &l1,const Line &l2){
return equal_double(l1.a*l2.c,l2.a*l1.c) && equal_double(l1.b*l2.c,l2.b*l1.c);
}
/*判断点是否在多边形内 如果点在多边形上也视为在里面*/
bool point_in_polygon(int vcnt,Point pointset[],Point p){
int cnt = 0;
Lineseg l(p,Point(1e10,p.y));
Point pn = pointset[vcnt];
pointset[vcnt] = pointset[0];
fr(i,0,vcnt){
if (onlineseg(Lineseg(pointset[i],pointset[i+1]),p)) return 1;
if (equal_double(pointset[i].y ,pointset[i+1].y)) continue;
int tmp = -1;
if (onlineseg(l,pointset[i])) tmp=i;
else if (onlineseg(l,pointset[i+1])) tmp =i+1;
if (tmp != -1 && equal_double(pointset[tmp].y,min(pointset[i].y,pointset[i+1].y))) cnt++;
if (tmp == -1 && Isintersect(l.s,l.e,pointset[i],pointset[i+1])) cnt++;
}
pointset[vcnt] = pn;
if (cnt&1) return 1;
return 0;
}
//已知三角形三个顶点的坐标,求三角形的面积
double triangleArea(Point p0, Point p1, Point p2) {
double k = p0.x * p1.y + p1.x * p2.y + p2.x * p0.y
- p1.x * p0.y - p2.x * p1.y - p0.x * p2.y;
return fabs(k/2);
}
//凸包的极角序
static bool gramhap_cmp(Point a,Point b){
double k = cg2d.multiply(q[0],a,b);
if (fabs(k)<eps) return cg2d.dist(q[0],a)<cg2d.dist(q[0],b);
else if (k>0) return 1;
else return 0;
}
//n为原来多边形的点的个数 返回凸包上的点的个数
int graham(int n,Point p[],Point chs[]){
int mny = p[0].y ,idx = 0;
fr(i,1,n){
if (p[i].y < mny) mny = p[i].y,idx = i;
else if (p[i].y == mny &&p[i].x <p[idx].x) idx = i;
}
Point tmp = p[idx];
p[idx] = p[0];p[0] = tmp;
sort(p+1,p+n,gramhap_cmp);
chs[0] = p[n-1];chs[1] = p[0];
int stop = 1,pos =1;
while(pos<=n-1){
double d = multiply(chs[stop],chs[stop-1],p[pos]);
if (d<=0) chs[++stop] = p[pos++];
else stop--;
}
return stop+1;
}
//计算凸包的周长
double convex_circumference(int n,Point p[]){
double ret = 0;
fr(i,0,n-1) ret += dist(p[i],p[i+1]);
ret += dist(p[n-1],p[0]);
return ret;
}
}cg2d;
Point chs[N];
bool cmp(Point a,Point b){
if (fabs(a.vi-b.vi)<eps) return a.li>b.li;
return a.vi<b.vi;
}
double best,bestcnt,bestleft;
int n;
bool vst[20];
bool check(){
int tot = 0;
double totl = 0;
fr( i , 0,n){
if (vst[i]) totl += p[i].li;
else q[tot++] = p[i];
}
int vcnt = cg2d.graham(tot,q,chs);
double needl = cg2d.convex_circumference(vcnt,chs);
if (needl < totl) {
bestleft = totl-needl;
return 1;
}
return 0;
}
bool ansbool[N];
void dfs(double money,int cnt,int pos){
if (money >best) return ;
if (money == best && cnt>bestcnt) return;
if (check()){
best = money;bestcnt = cnt;
fr( i , 0,n) ansbool[i] = vst[i];
return ;
}
fr(i,pos+1,n){
vst[i] = 1;
dfs(money+p[i].vi,cnt+1,i);
vst[i] = 0;
}
}
void solve(){
best = 10000000000.0;
bestcnt = 20;
dfs(0,0,-1);
printf("Cut these trees:");
vector<int> v;
fr(i , 0,n){
if (ansbool[i]){
v.push_back(p[i].pos);
}
}
sort(v.begin(),v.end());
fr(i , 0,v.size()){
printf(" %d",v[i]);
}
printf("\nExtra wood: %.2f\n",bestleft);
}
int main(){
int cas = 0;
while(sfint(n),n){
if (cas>0) printf("\n");
printf("Forest %d\n",++cas);
fr(i , 0, n){
p[i].read();
p[i].pos = i+1;
}
sort(p,p+n,cmp);
solve();
}
return 0;
}