題目連結:http://poj.org/problem?id=1873
題意:給出一些樹,每棵樹有座標,高度,以及價值,要求砍掉一些樹,用那些木材,將其它樹圍起來,要求花最小的代價,代價相同,要求砍掉最少的樹。
最後輸出要砍掉哪幾棵樹和最後剩下多少長度的木材。
輸入:
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
n棵樹,每行一棵,座標,價值,高度。
因為樹的棵數不多,所以可以直接狀態壓縮枚舉來做,枚舉時要注意判斷砍0棵樹的情況。
2^15次方,然後對剩下的樹求一次凸包,求出凸包周長,判斷木材是否夠用,記錄最優解。
#include <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <vector>#include <queue>#include <cmath>using namespace std;const double eps = 1e-8;const double PI = acos(-1.0);int sgn(double x) {if(fabs(x) < eps)return 0;if(x < 0)return -1; else return 1; }struct Point { double x,y, v, l;Point(){} Point(double _x,double _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; } void transXY(double B) { double tx = x,ty = y; x = tx*cos(B) - ty*sin(B); y = tx*sin(B) + ty*cos(B); } };struct Line { Point s,e; Line(){} Line(Point _s,Point _e) { s = _s;e = _e; } pair<int,Point> operator &(const Line &b)const {Point res = s;if(sgn((s-e)^(b.s-b.e)) == 0) {if(sgn((s-b.e)^(b.s-b.e)) == 0) return make_pair(0,res); else return make_pair(1,res); } double t = ((s-b.s)^(b.s-b.e))/((s-e)^(b.s-b.e));res.x += (e.x-s.x)*t; res.y += (e.y-s.y)*t; return make_pair(2,res); } };double dist(Point a,Point b) { return sqrt((a-b)*(a-b)); }const int MAXN = 1010; Point list[MAXN]; int Stack[MAXN], top;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; } } Point tree[20];int main() {int n;int kase = 1; while(~scanf("%d", &n), n) { printf("Forest %d\n", kase++); int i, j; for(i = 0; i < n; i++) { scanf("%lf %lf %lf %lf", &tree[i].x, &tree[i].y, &tree[i].v, &tree[i].l); } double ansc = 10000000, ansl;int anst[20];int num = 0; for(i = 0; i < 1 << n; i++) { int cnt = 0; double len1 = 0, len2 = 0, cost = 0; for(j = 0; j < n; j++) { if(i >> j & 1) { list[cnt++] = tree[j]; } else { len2 += tree[j].l; cost += tree[j].v; } } if(cnt == 0) continue; //砍0棵樹 Graham(cnt); for(j = 0; j < top; j++) { len1 += dist(list[Stack[j]], list[Stack[(j + 1) % top]]); } if(len1 <= len2) { if(ansc > cost) { ansc = cost; ansl = len2 - len1; num = 0; for(j = 0; j < n; j++) { if(!(i >> j & 1)) { anst[num++] = j; } } } else if(ansc == cost){ if(n - cnt < num) { ansc = cost; ansl = len2 - len1; num = 0; for(j = 0; j < n; j++) { if(i >> j & 1) { anst[num++] = j; } } } } } } printf("Cut these trees:"); for(i = 0; i < num; i++) { printf(" %d", anst[i] + 1); } printf("\n"); printf("Extra wood: "); printf("%.2lf\n\n", ansl); }return 0;}