HDU--3685[Rotational Painting] 求重心+凸包

25 篇文章 0 订阅
8 篇文章 1 订阅

稳定的定义:
多边形玻璃板的重心向水平面作垂线时,垂足落在支撑边所在的线段上(不包括线段端点)。


对多边形支撑边的枚举:
由于有可能出现凹多边形,因此可供选择的“支撑边”包括该多边形的凸包的所有边。

 

思路:
先求重心,然后再求凸包,枚举凸包的每条边判断是否可以是支撑边。(注意重心到某条支撑边的交点在端点算不合法)

CODE:
/*求重心+凸包*/
/*AC代码:187ms*/
#include <iostream>
#include <cstdio>
#include <algorithm>
#define MAXN 50005
using namespace std;
/*==================================================*\ 
| Graham 求凸包 O(N * logN) 
| CALL: nr = graham(pnt, int n, res); res[]为凸包点集; 
\*==================================================*/ 
struct point {double x,y;};
struct point pnt[MAXN],res[MAXN],center; 
int N,M;
bool mult(point sp,point ep,point op)
{ 
	return (sp.x-op.x)*(ep.y-op.y)>=(ep.x-op.x)*(sp.y-op.y); 
} 
bool operator<(const point &l,const point &r)
{ 
	return l.y<r.y||(l.y==r.y&&l.x<r.x); 
}
int graham(point pnt[],int n,point res[])//注意都是从0开始存
{ 
	int i,len,k=0,top=1; 
	sort(pnt,pnt+n); 
	if (n == 0) return 0; res[0]=pnt[0]; 
	if (n == 1) return 1; res[1]=pnt[1]; 
	if (n == 2) return 2; res[2]=pnt[2]; 
	for(i=2;i<n;i++) 
	{ 
		while(top&&mult(pnt[i],res[top],res[top-1]))
			top--; 
		res[++top]=pnt[i]; 
	} 
	len=top;res[++top]=pnt[n-2]; 
	for(i=n-3;i>=0;i--) 
	{ 
		while(top!=len&&mult(pnt[i],res[top],res[top-1]))
			top--; 
		res[++top]=pnt[i]; 
	} 
	return top;       // 返回凸包中点的个数 
}
//----------------------------------------------------------//
//----------------------------------------------------------// 
// 求多边形重心 
// INIT: pnt[]已按顺时针(或逆时针)排好序; 
// CALL: res = bcenter(pnt, n); 
//----------------------------------------------------------// 
point bcenter(point pnt[], int n){ 
	point p, s; 
	double tp, area = 0, tpx = 0, tpy = 0; 
	p.x = pnt[0].x; p.y = pnt[0].y; 
	for (int i = 1; i <= n; ++i) {   // point: 0 ~ n-1 
		s.x = pnt[(i == n) ? 0 : i].x; 
		s.y = pnt[(i == n) ? 0 : i].y; 
		tp = (p.x * s.y - s.x * p.y); area += tp / 2; 
		tpx += (p.x + s.x) * tp; tpy += (p.y + s.y) * tp; 
		p.x = s.x; p.y = s.y; 
	} 
	s.x = tpx / (6 * area); s.y = tpy / (6 * area); 
	return s; 
} 
void Init()
{
	int i;
	scanf("%d",&N);
	for(i=0;i<N;i++)
		scanf("%lf%lf",&pnt[i].x,&pnt[i].y); 
	center=bcenter(pnt,N);
	M=graham(pnt,N,res);
}
double mul(point p1,point p2)//点积
{
	return p1.x*p2.x+p1.y*p2.y;
}
bool Judge(point p1,point p2)//判断两个夹角是否都是锐角
{
	point a,b;
	double x1,x2,y1,y2,xo,yo;
	x1=p1.x;y1=p1.y;
	x2=p2.x;y2=p2.y;
	xo=center.x;yo=center.y;
	a.x=(x2-x1);a.y=(y2-y1);
	b.x=(xo-x1);b.y=(yo-y1);
	if(mul(a,b)<=0) return false;
	a.x=(x1-x2);a.y=(y1-y2);
	b.x=(xo-x2);b.y=(yo-y2);
	if(mul(a,b)<=0) return false;
	return true;
} 
void Solve()
{
	int i,ans=0;
	for(i=0;i<M-1;i++)
		ans+=Judge(res[i],res[i+1]);
	ans+=Judge(res[M-1],res[0]);
	//printf("&%.3lf %.3lf\n",center.x,center.y);
	printf("%d\n",ans);
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		Init();
		Solve();
	}
return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__简言

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值