题目链接:http://poj.org/problem?id=1873
Description
Input
Output
Sample Input
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
Sample Output
Forest 1 Cut these trees: 2 4 5 Extra wood: 3.16 Forest 2 Cut these trees: 2 Extra wood: 15.00
Source
有n棵树(n<=15), 每棵树的价值为 v , 砍掉这个树能得到长度为 l 的木板。
现在要砍掉其中的一些树,用来造木板把剩余的所有树围起来,(每颗树只有砍与不砍两种状态),
问要造这种木板最少需要消耗多少的价值(砍掉的树的 v 的和),
输出需要砍掉哪些树和剩余的木板长度(砍掉的树形成的木板并且包围未砍掉的树没有用完的木板长度)。
PS:
n比较小,我们可以枚举砍掉的树!
代码如下:
#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <iostream> using namespace std; #define INF 0x3f3f3f3f const double eps = 1e-8; const double PI = acos(-1.0); const int MAXN = 1010; int sgn(double x) { if(fabs(x) < eps)return 0; if(x < 0)return -1; else return 1; } struct Point { int x, y; int v, l; Point() {} Point(int _x, int _y) { x = _x; y = _y; } Point operator -(const Point &b)const { return Point(x - b.x,y - b.y); } //叉积 double operator ^(const Point &b)const { return x*b.y - y*b.x; } //点积 double operator *(const Point &b)const { return x*b.x + y*b.y; } }; Point list[MAXN], tr[MAXN]; int Stack[MAXN], top; //*两点间距离 double dist(Point a,Point b) { return sqrt((a-b)*(a-b)); } //相对于list[0]的极角排序 bool _cmp(Point p1,Point p2) { double tmp = (p1-list[0])^(p2-list[0]); if(sgn(tmp) > 0)return true; else if(sgn(tmp) == 0 && sgn(dist(p1,list[0]) - dist(p2,list[0])) <= 0) return true; else return false; } void Graham(int n) { Point p0; int k = 0; p0 = list[0]; //找最下边的一个点 for(int i = 1; i < n; i++) { if( (p0.y > list[i].y) || (p0.y == list[i].y && p0.x > list[i].x) ) { p0 = list[i]; k = i; } } swap(list[k],list[0]); sort(list+1,list+n,_cmp); if(n == 1) { top = 1; Stack[0] = 0; return; } if(n == 2) { top = 2; Stack[0] = 0; Stack[1] = 1; return ; } Stack[0] = 0; Stack[1] = 1; top = 2; for(int i = 2; i < n; i++) { while(top > 1 && sgn((list[Stack[top-1]]-list[Stack[top-2]])^(list[i]-list[Stack[top-2]])) <= 0) top--; Stack[top++] = i; } } int main() { int n; int cas = 0; int ans[20]; while(scanf("%d",&n) && n) { int x, y, v, l; int pnum;//最少砍掉的数量 double rest = 0;//剩余的长度 int min_tree = n+1; int min_val = INF; for(int i = 0; i < n; i++) { scanf("%d%d%d%d",&tr[i].x,&tr[i].y,&tr[i].v,&tr[i].l); } for(int i = 0; i < (1<<n); i++) { double len, val; int num; len = num = val = 0; int k = 0; int vis[27]; memset(list,0,sizeof(list)); for(int j = 0; j < n; j++) { vis[j] = 0; if(i & (1<<j))//砍掉 { val+=tr[j].v; len+=tr[j].l; num++; vis[j] = 1; } else { list[k++] = Point(tr[j].x, tr[j].y); } } if(val > min_val || (val==min_val && min_tree < num)) { continue; } if(len == 0 && k != 1) { continue; } Graham(k); double res = 0;//凸包周长 for(int h = 0; h < top-1; h++) { res+=dist(list[Stack[h]],list[Stack[h+1]]); } res+=dist(list[Stack[0]],list[Stack[top-1]]); //printf("len::%.2lf >>res:%.2lf, ans::%.2lf\n",len,res,len-res); if(res <= len)//能包围 { min_val = val; min_tree = num; rest = len - res; pnum = 0; for(int f = 0; f < n; f++) { if(vis[f]) { ans[pnum++] = f; } } } } printf("Forest %d\n",++cas); printf("Cut these trees:"); for(int i = 0; i < pnum; i++) { printf(" %d",ans[i]+1); } printf("\nExtra wood: %.2f\n\n", rest); } }