poj1271 Nice Milk (dfs+半平面交)

//题目链接: http://poj.org/problem?id=1271
//题意:给一个凸多边形的面包,和一瓶深度为h的牛奶,要沾牛奶k次,求可以沾到牛奶的面包的面积
//思路:保存各个边被牛奶沾后的位置,用dfs找出要沾的边,然后对多边形执行k个半面交操作(这里我用O(n^2)的联机算法),求面积。。。
//曾经纠结了半天,现在重写了一下,没特判wa了一次。。。。。。


//代码如下:  用时:547 MS


#include<iostream>
#include<cstdio>
#include<math.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<cstdlib>
#define eps 1e-8
#define  inf 1e10
#define maxn 40
#define xmult(a,b,c) (a.x-c.x)*(b.y-c.y)-(a.y-c.y)*(b.x-c.x)
struct point{double x,y;}points[maxn],save[maxn],temp[maxn];
struct line{point s,e;}lines[maxn];
double minn;
int n,k,ntemp;
double h;
int witch[maxn];
point intersection(point p1,point p2,double a,double b,double c)
{
	double s=fabs(a*p1.x+b*p1.y+c);
	double e=fabs(a*p2.x+b*p2.y+c);
	point ret;
	ret.x=(p1.x*e+p2.x*s)/(s+e);
	ret.y=(p1.y*e+p2.y*s)/(s+e);
	return ret;
}
void getline(point p1,point p2,double &a,double &b,double &c)
{
	a=p2.y-p1.y;
	b=p1.x-p2.x;
	c=p2.x*p1.y-p2.y*p1.x;
}
bool zero(double x){return x>0? x<eps:x>-eps;}
bool equal_p(const point &a,const point &b)
{
	return zero(a.x-b.x)&&zero(a.y-b.y);
}
double area(point *p,int n)
{
	int i;double res=0;
	for(i=0;i<n;i++)res+=p[(i+1)%n].x*p[i].y-p[(i+1)%n].y*p[i].x;
	return fabs(res)/2.0;
}
double min(double a,double b){return a>b? b:a;}
void my_copy(point *a,point *b,int n)
{
	int i;
	for(i=0;i<n;i++)
	{
		a[i].x=b[i].x;a[i].y=b[i].y;
	}
}
void pri(point *p,int n)
{
	int i;for(i=0;i<n;i++)printf("%.2lf %.2lf\n",p[i].x,p[i].y);
}
void cut()
{
	int i,j,t,m;
	double a, b, c;
	int cnt;
	for(t=0;t<k;t++)
	{
		cnt=0;
		getline(lines[witch[t]].s,lines[witch[t]].e,a,b,c);
		for(i=0;i<ntemp;i++)//逆时针用<= 号和 < 号
		{
			if(a*temp[i].x+b*temp[i].y+c<=0)save[cnt++]=temp[i];
			else
			{
				if(a*temp[(i-1+ntemp)%ntemp].x+b*temp[(i-1+ntemp)%ntemp].y+c<0)
					save[cnt++]=intersection(temp[i],temp[(i-1+ntemp)%ntemp],a,b,c);
				if(a*temp[(i+1)%ntemp].x+b*temp[(i+1)%ntemp].y+c<0)
					save[cnt++]=intersection(temp[i],temp[(i+1)%ntemp],a,b,c);
			}
		}
		
		if(t!=k-1)
		{
			//  cnt=unique(save,save+cnt,equal_p)-save;
			ntemp=cnt;
			my_copy(temp,save,cnt);
		}
		else
		{
			ntemp=n;
			my_copy(temp,points,n);
		}
	}
	minn=min(minn,area(save,cnt));
}
void dfs(int time,int s)
{
	int i;
	if(time==k) {cut();return;}
	for(i=s;i<n;i++)
	{
		witch[time]=i;
		dfs(time+1,i+1);
	}
}
double distance(point p1,point p2)
{
	return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}
void change()
{
	int i;
	for(i=0;i<n;i++)
	{
		point p1=points[i],p2=points[(i+1)%n];
		point dir;
		dir.x=p2.x-p1.x;
		dir.y=p2.y-p1.y;
		double dis=distance(p1,p2);
		lines[i].s.x=p1.x-dir.y*h/dis;
		lines[i].s.y=p1.y+dir.x*h/dis;
		lines[i].e.x=p2.x-dir.y*h/dis;
		lines[i].e.y=p2.y+dir.x*h/dis;
	}
}
int main()
{
	while(scanf("%d%d%lf",&n,&k,&h),n+k+h)
	{
		int i;
		for(i=0;i<n;i++)
		{
			scanf("%lf%lf",&points[i].x,&points[i].y);
		}
		my_copy(temp,points,n);
		change();
		minn=inf;
		ntemp=n;//忘了初始化这个检查了好久
		if(k==0)//没特判wa了
		{
			printf("0.00\n");
			continue;
		}
		if(k<n)
			dfs(0,0);
		else
		{
			k=n;
			for(i=0;i<n;i++)witch[i]=i;
			cut();
		}
		printf("%.2lf\n",area(points,n)-minn);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值