POJ 1873 The Fortified Forest 凸包+搜索

题目让你用最少的花费(砍掉树木的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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值