题目链接: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
*/