【POJ1873】The Fortified Forest-凸包+枚举方案
测试地址:The Fortified Forest
题目大意:有N(2≤N≤15)棵树,每棵树有一个坐标(xi,yi),价值vi,长度li,要砍掉一些树建成围栏防护其他的树,求一个使砍掉的树的价值之和最小的方案,如果有多个方案满足条件,求砍掉的树最少的,输出这种方案中要砍的树的编号,顺便还要输出这种方案下建成围栏之后多余的木材长度。
做法:考虑到N很小,所以我们就枚举方案求最佳方案就可以了。要防护一些树就要把这些树包围起来,而且使得该封闭图形的周长最小,那么显然就是求凸包了,可以用Graham-Scan算法。一个方案合法当且仅当砍掉的树的长度和大于等于围栏的最小长度,在方案合法的基础上求最优方案即可。注意剩下的树为1棵或2棵的情况(只剩1棵树围栏长度为0,剩2棵树围栏长度为两棵树之间距离的两倍),以及C++和G++保留小数的细微不同(C++下用%.2lf,G++下用%.2f),否则会WA到死(像我一样)。
以下是本人代码(话说我的代码风格越来越丑了......):
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <cmath>
#define inf 1000000000
#define eps 1e-8
using namespace std;
int n,tot,anstot=inf,ansval=inf,totval,v[20];
double x[20],y[20],l[20],totlen,anslen,dis;
bool choice[20]={0},ans[20]={0};
struct point {double x,y;} p[20];
double multi(point a,point b,point c)
{
return (a.x-c.x)*(b.y-c.y)-(b.x-c.x)*(a.y-c.y);
}
double dist(point a,point b)
{
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
bool equal(double a,double b)
{
if (fabs(a-b)<=eps) return 1;
else return 0;
}
bool cmp(point a,point b)
{
double s=multi(a,b,p[1]);
if (!equal(s,0)) return s>0;
else return dist(p[1],a)<dist(p[1],b);
}
double graham_scan()
{
for(int i=1;i<=tot;i++)
if (p[i].x<p[1].x||(equal(p[i].x,p[1].x)&&p[i].y<p[1].y))
{
swap(p[i].x,p[1].x);
swap(p[i].y,p[1].y);
}
sort(p+2,p+tot+1,cmp);
int st[20],top=2;
st[1]=1,st[2]=2;
for(int i=3;i<=tot;i++)
{
double s=multi(p[st[top]],p[i],p[st[top-1]]);
while(s<0&&!equal(s,0)&&top>1)
{
top--;
s=multi(p[st[top]],p[i],p[st[top-1]]);
}
st[++top]=i;
}
double len=0;
for(int i=2;i<=top;i++)
len+=dist(p[st[i]],p[st[i-1]]);
len+=dist(p[st[top]],p[st[1]]);
return len;
}
void solve()
{
tot=totval=0;totlen=0;
for(int i=1;i<=n;i++)
{
if (choice[i])
{
totval+=v[i];
totlen+=l[i];
}
else
{
p[++tot].x=x[i],p[tot].y=y[i];
}
}
if (totval>ansval) return;
if (totval==ansval&&n-tot>=anstot) return;
if (tot==1) dis=0;
else dis=graham_scan();
if (totlen<dis&&!equal(totlen,dis)) return;
ansval=totval,anstot=n-tot,anslen=totlen-dis;
for(int i=1;i<=n;i++)
ans[i]=choice[i];
}
int main()
{
int t=0;
while(scanf("%d",&n)&&n)
{
++t;
if (t>1) printf("\n\n");
anstot=inf,ansval=inf;
for(int i=1;i<=n;i++)
scanf("%lf%lf%d%lf",&x[i],&y[i],&v[i],&l[i]);
for(int i=1;i<(1<<n);i++)
{
int x=i;
for(int j=1;j<=n;j++)
choice[j]=x&1,x>>=1;
solve();
}
printf("Forest %d\nCut these trees: ",t);
for(int i=1;i<=n;i++)
if (ans[i]) printf("%d ",i);
printf("\nExtra wood: %.2lf",anslen);
}
return 0;
}