【poj】1873: The Fortified Forest (凸包+状态压缩+各种搞事)

1 篇文章 0 订阅
1 篇文章 0 订阅

题目描述:

The Fortified Forest

Time Limit: 1000MS Memory Limit: 30000K
Total Submissions: 7818 Accepted: 2149

Description

Once upon a time, in a faraway land, there lived a king. This king owned a small collection of rare and valuable trees, which had been gathered by his ancestors on their travels. To protect his trees from thieves, the king ordered that a high fence be built around them. His wizard was put in charge of the operation. 
Alas, the wizard quickly noticed that the only suitable material available to build the fence was the wood from the trees themselves. In other words, it was necessary to cut down some trees in order to build a fence around the remaining trees. Of course, to prevent his head from being chopped off, the wizard wanted to minimize the value of the trees that had to be cut. The wizard went to his tower and stayed there until he had found the best possible solution to the problem. The fence was then built and everyone lived happily ever after. 

You are to write a program that solves the problem the wizard faced. 

样例输入:

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

样例输出:

Forest 1
Cut these trees: 2 4 5 
Extra wood: 3.16

Forest 2
Cut these trees: 2 
Extra wood: 15.00

解题思路:

       因为n<=15,所以范围不大。两种方法:1.搜索+凸包 2.状态压缩+凸包  事实上,因为加上各种剪枝后时间效率会大大增加,所以搜索可能反而会更快一些。但是由于我一向不喜欢暴力,所以我强迫自己用第二种方法做。

简单介绍一下第二种方法:

        1.每棵树的状态可用1或0表示,1表示砍掉这棵树,0表示不砍。那么这个树林的情况就可以用二进制数来表达。比如:100101,表示第一棵、第三棵、第六棵会被砍掉(倒过来看)。同时可以将二进制数转化为十进制数。比如:100101(还是那个数)就是十进制中的37。所以我们任意一个十进制数,都可以用转为二进制数,从而表达一片树林的状态。这样一来,我们直接可以for循环,从0到2的n次方减1(最多有n棵树被砍掉)

        2.每一个新的状况对应着新的凸包,所以要及时更新变量和数组。

        3.凸包要特判一下只剩下0棵树和1棵树的情况,不然会卡住

        4.这道题很容易wa的原因在于——长度要用double类型,千万别用int!

        5.由于有多组数据,注意要及时更新。总而言之,水题一道。

附上c++代码:

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node
{
	double x,y,v,l;
}a[2100],p[2100],sta[2100];
double multi(node n1,node n2,node no){return (n1.x-no.x)*(n2.y-no.y)-(n2.x-no.x)*(n1.y-no.y);}
double dis(node n1,node n2){return sqrt((n1.x-n2.x)*(n1.x-n2.x)+(n1.y-n2.y)*(n1.y-n2.y));}
int n,dd=0,top=0,nn=0,k=99999999,kk=0;
double ans=0,aans=0;
double sum=0,len=0,s=0;
int maxx;
bool bo[20];
int aa[20];
bool cmp(node n1,node n2)
{
	double t=multi(n1,n2,p[1]);
	if(t==0 && dis(n1,p[1])<dis(n2,p[1]))return true;
	return t>0;
}
void graghm()
{
	top=0;sort(p+2,p+nn+1,cmp);
	for(int i=1;i<=2;i++)sta[++top]=p[i];
	for(int i=3;i<=nn;i++)
	{
		while(top>2 && multi(sta[top],p[i],sta[top-1])<=0)top--;
		sta[++top]=p[i];
	}
}
void make(int d)
{
	memset(bo,true,sizeof(bo));aans=0,s=0;len=0;kk=n;
	int i=1;
	while(d!=0)
	{
		if(d%2==1)bo[i]=false,s+=a[i].l,kk--;
		d/=2;
		i++;
	}
	for(int i=1;i<=n;i++)if(bo[i]==true)aans+=a[i].v;
	nn=0;
	for(int i=1;i<=n;i++)
	{
		if(bo[i]==true)
		{
			p[++nn]=a[i];
			if(p[nn].y<p[1].y || (p[nn].y==p[1].y && p[nn].x<p[1].x))swap(p[1],p[nn]);
		}
	}
	if(kk==1)len=0;
	if(kk>1)
	{
		graghm();
		for(int i=2;i<=top;i++)len+=dis(sta[i],sta[i-1]);
		len+=dis(sta[1],sta[top]);
	}
}
int main()
{
	int t=0;
	while(scanf("%d",&n)!=EOF)
	{
		if(n==0)break;t++;
		ans=0;k=999999999;dd=0;
		for(int i=1;i<=n;i++)
		{
			scanf("%lf%lf%lf%lf",&a[i].x,&a[i].y,&a[i].v,&a[i].l);
		}
		maxx=(1<<n)-1;
		for(int i=0;i<maxx;i++)
		{
			make(i);
			if((ans<aans && s-len>=0) || (ans==aans && kk<k))
			{
				ans=aans;sum=s-len;dd=0;k=kk;
				for(int j=1;j<=n;j++)if(!bo[j])aa[++dd]=j;
			}
		}
		printf("Forest %d\n",t);
		printf("Cut these trees: ");for(int i=1;i<=dd;i++)printf("%d ",aa[i]);
		printf("\nExtra wood: %.2f\n\n",sum);
	}
	return 0;
}

如果还有疑问,欢迎向我的邮箱提问——gongxiaohanxh@126.com,我都乐意回答的哦~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值