版权声明:转载注明下出处就行了。 https://blog.csdn.net/LJD201724114126/article/details/84038337
题目链接:poj 1873
题意:给出n颗数,每棵树有四个值 xi, yi, vi, li ,分别表示坐标xi,yi,值vi,长度li,现在让你从中选出一些树,将剩余的树围起来,保证选出来的树的总和值要尽量小,假如有多种值,取较少数目的树。
输出:被选出来作为围墙的树,以及建造围墙剩余的长度。
参考链接:https://www.cnblogs.com/liuxin13/p/4908324.html
题解;因为题目数据不大,可以直接二进制枚举,那么剩余的树自然作为被围的树,求这些剩余的树的凸包,就行了。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
#define INF 0x3f3f3f3f
const int maxn=20;
struct point{
double x,y;
point(){}
point(double _x,double _y){
x=_x;y=_y;
}
}p[maxn],ch[maxn],data[maxn];
point operator + (point a,point b) { return point(a.x+b.x,a.y+b.y);}
point operator - (point a,point b) { return point(a.x-b.x,a.y-b.y);}
point operator * (point a,double p) { return point(a.x*p,a.y*p);}
point operator / (point a,double p) { return point(a.x/p,a.y/p);}
bool operator < (const point &a,const point &b){
return a.x<b.x||(a.x==b.x&&a.y<b.y);
}
const double esp=1e-8;
int dcmp(double x)
{
if(fabs(x)<esp) return 0;
else return x<0?-1:1;
}
bool operator == (const point &a,const point &b){
return dcmp(a.x-b.x)==0&&dcmp(a.y-b.y)==0;
}
double Cross(point a,point b) { return a.x*b.y-a.y*b.x;}
double Length(point a){ return sqrt(a.x*a.x*1.0+a.y*a.y*1.0);}
bool cmp(point a,point b){
return (a.y<b.y||(a.y==b.y&&a.x<b.x));
}
int tot;
int val,treenum; ///价值,被选树的数量
double lastlen; ///剩余的长度
int vi[maxn],li[maxn]; ///存储每棵树的价值和长度
void andrew(int n) ///凸包模板
{
sort(p,p+n,cmp);
tot=-1;
if(n==2){
ch[0]=p[0];
ch[1]=p[1];tot=1;
return;
}
if(n==1){
ch[0]=p[0];tot=0;
return;
}
for(int i=0;i<n;i++)
{
while(tot>0&&Cross(ch[tot]-ch[tot-1],p[i]-ch[tot-1])<=0)
tot--;
ch[++tot]=p[i];
}
for(int i=n-2,k=tot;i>=0;i--){
while(tot>k&&Cross(ch[tot]-ch[tot-1],p[i]-ch[tot-1])<=0)
tot--;
ch[++tot]=p[i];
}
}
bool solve(int bitnum,int n)
{
int cut_val=0,cnt=0; ///选出来被砍的树的总价值,剩余树的数目
double cut_len=0; ///选出来被砍的树的总长度
for(int i=0;i<n;i++)
{
if(bitnum&1){
cut_val+=vi[i];
cut_len+=li[i];
}
else p[cnt++]=data[i]; ///没被选中,存进p结构体,等下去凸包
bitnum>>=1;
}
andrew(cnt); ///求凸包
double len=0; ///计算凸包长度
for(int i=0;i<tot;i++){
len+=Length(ch[i+1]-ch[i]);
}
len+=Length(ch[tot]-ch[0]);
if(dcmp(cut_len-len>=0&&(val>cut_val||(val==cut_val&&treenum>n-cnt))))
{
lastlen=cut_len-len;
val=cut_val;
treenum=n-cnt;
return 1;
}
return 0;
}
int main()
{
int n,T=0;
while(scanf("%d",&n)&&n)
{
val=treenum=INF;
for(int i=0;i<n;i++)
scanf("%lf%lf%d%d",&data[i].x,&data[i].y,&vi[i],&li[i]);
int item=(1<<n)-1; ///二进制枚举
int ans;
for(int i=1;i<item;i++) ///因为不可能全选,故<
{
if(solve(i,n)) ans=i;
}
if(T) puts("");
printf("Forest %d\n", ++T);
printf("Cut these trees:");
for(int i=1; ans; i++)
{
if(ans & 1)
printf(" %d", i);
ans >>= 1;
}
printf("\nExtra wood: %.2f\n", lastlen);
}
return 0;
}