【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;
}


posted @ 2017-04-03 11:16  Maxwei_wzj  阅读(105)  评论(0编辑  收藏  举报