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

题目链接:http://poj.org/problem?id=1873

题目大意:给出n棵树的位置(x,y)坐标,价值v和长度l。让你从中选择一些树,砍掉他们将其他的树围起来。

要求砍的这些树的价值之和最小。按顺序输出砍的树。如果价值相同,输出砍的树最少的的方案。最后在输出一个:砍掉的这些树,围城围栏之后,还剩下多少长度的木头。

思路:一开始很懵逼的。。但是看数据范围这么小,还以为是状压DP,但是出在了凸包专题中,就想怎么用凸包解决(最后证明就是凸包,什么状压DP都是假的)。但毕竟还是WF的题。需要考虑的东西有点多,DFS搜索所有砍树的情况,对于每种情况,都计算一遍所需要的凸包(即求所谓长度),然后从中选择所需要val最少的那个情况。从砍1棵树开始枚举的话,可以直接得出最少的砍树情况。

ACCode:

//#pragma comment(linker, "/STACK:1024000000,1024000000")
  
#include<stdio.h>
#include<string.h> 
#include<math.h> 
   
#include<map>  
#include<set>
#include<deque> 
#include<queue> 
#include<stack> 
#include<bitset>
#include<string> 
#include<fstream>
#include<iostream> 
#include<algorithm> 
using namespace std; 
  
#define ll long long 
#define Pair pair<int,int>//,pair<int,int> >
//#define max(a,b) (a)>(b)?(a):(b)
//#define min(a,b) (a)<(b)?(a):(b)
#define clean(a,b) memset(a,b,sizeof(a))// 水印
//std::ios::sync_with_stdio(false);
//  register
const int MAXN=3e2+10;
const int INF32=0x3f3f3f3f;
const ll INF64=0x3f3f3f3f3f3f3f3f;
const ll mod=1e9+7;
const double EPS=1.0e-8;
const double PI=acos(-1.0);

//需要自己写文件读入scanf和getchar()都不能用。
//freopen("1.txt","r",stdin);
namespace fastIO {
    #define BUF_SIZE 100000
    //fread -> read
    bool IOerror = 0;
    inline char nc() {
        static char buf[BUF_SIZE], *p1 = buf + BUF_SIZE, *pend = buf + BUF_SIZE;
        if(p1 == pend) {
            p1 = buf;
            pend = buf + fread(buf, 1, BUF_SIZE, stdin);
            if(pend == p1) {
                IOerror = 1;
                return -1;
            }
        }
        return *p1++;
    }
    inline bool blank(char ch) {
        return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';
    }
    inline void read(int &x) {
        char ch;
        while(blank(ch = nc()));
        if(IOerror) return;
        for(x = ch - '0'; (ch = nc()) >= '0' && ch <= '9'; x = x * 10 + ch - '0');
    }
    #undef BUF_SIZE
};
using namespace fastIO;

struct Point{
	double x,y,v,l;
	Point(double _x=0,double _y=0,double _v=0,double _l=0){
		x=_x;y=_y;v=_v;l=_l;
	}
	friend Point operator + (const Point &a,const Point &b){
		return Point(a.x+b.x,a.y+b.y);
	}
	friend Point operator - (const Point &a,const Point &b){
		return Point(a.x-b.x,a.y-b.y);
	}
	friend double operator ^ (Point a,Point b){//向量叉乘 
		return a.x*b.y-a.y*b.x;
	}
};
struct V{
	Point start,end;double ang;
	V(Point _start=Point(0,0),Point _end=Point(0,0),double _ang=0.0){
		start=_start;end=_end;ang=_ang;
	}
	friend V operator + (const V &a,const V &b){
		return V(a.start+b.start,a.end+b.end);
	}
	friend V operator - (const V &a,const V &b){
		return V(a.start-b.start,a.end-b.end);
	}
};
Point v[MAXN],p[MAXN];
int ans[MAXN],stk[MAXN];
int vst[MAXN];//标记是否被砍掉 1砍掉,0不砍 
double extra,minv;
int n,nown,ansn;

int Cmp(Point a,Point b){
	if(a.y==b.y)
		return a.x<b.x;
	return a.y<b.y;
}
double Multi(Point a,Point b,Point c,Point d){
	b=b-a;d=d-c;
	return b^d;
}
int turn_left(Point a,Point b,Point c){
	return Multi(a,b,b,c)>0;
}
double Dist(Point a,Point b){
	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
double Judge(){//求取凸包 
	int top=1;stk[0]=0;
	sort(p,p+nown,Cmp);//对加入点集p的点进行排列。 
	for(int i=1;i<nown;){
		if(top==1||turn_left(p[stk[top-2]],p[stk[top-1]],p[i])) stk[top++]=i++;
		else --top;
	}int t_top=top;
	for(int i=nown-2;i>=0;){
		if(top==t_top||turn_left(p[stk[top-2]],p[stk[top-1]],p[i])) stk[top++]=i--;
		else --top;
	}double ret=0.0;//获得凸包的围墙长度。 
	for(int i=0;i<top-1;++i){
		ret+=Dist(p[stk[i]],p[stk[i+1]]); 
	}return ret;
}
void dg(int deep,int num,double val,double len,int from){//DFS,枚举所有的方法. 
	//		0		i			0			0		0
	if(deep==num){
		if(val>minv) return ;//所需要的价值更大 舍去 
		int temp=0;
		for(int k=0;k<n;++k){
			if(!vst[k]){
				p[temp++]=v[k];//向点集p中添加没有被砍掉的点。 
			}
		}double dis=Judge();//需要的围墙长度 
		if(len>=dis){//长度符合要求 
			if(val<minv){//所需要的价值更小 
				minv=val;//刷新价值 
				extra=len-dis;//损失的长度 
				ansn=0; 
				for(int k=0;k<n;++k){//ans数组刷新 
					if(vst[k]){
						ans[ansn++]=k+1;
					}
				}
			}
		}return ;
	}
	for(int i=from;i<(n-(num-deep-1));++i){//搜索 
		vst[i]=1;
		dg(deep+1,num,val+v[i].v,len+v[i].l,i+1);
		vst[i]=0;
	}
}
int main(){
	int cnt=1;
	while(~scanf("%d",&n)){
		if(n==0) break;
		for(int i=0;i<n;++i){
			scanf("%lf%lf%lf%lf",&v[i].x,&v[i].y,&v[i].v,&v[i].l);
		}minv=2e9;
		for(int i=1;i<=n-1;++i){
			nown=n-i;
			memset(vst,0,sizeof(vst));
			dg(0,i,0,0,0);
		}printf("Forest %d\n",cnt++);
		printf("Cut these trees:");
		for(int i=0;i<ansn;++i){
			printf(" %d",ans[i]);
		}putchar('\n');
		printf("Extra wood: %.2f\n\n",extra);
	}
}
/*
6
 0  0  8  3
 1  4  3  2
 2  1  7  1
 4  1  2  3
 3  5  4  6
 2  3  9  8
3
 3  0 10  2
 5  5 20 25
 7 -3 30 32
0

*/

猜你喜欢

转载自blog.csdn.net/qq_40482358/article/details/88081113