洛谷P3829 信用卡凸包

本文介绍了一种计算带有圆角的信用卡形状的凸包算法。通过分析信用卡形状的特点,利用几何和代数方法推导出计算公式,实现了信用卡形状的精确计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

nnn张信用卡,原本每张信用卡都是水平间距为bbb,垂直间距为aaa的矩形,现在要将四个角打磨成直角,使角变为14\frac{1}{4}41圆弧,并且每张卡的圆弧的半径都是rrr。给出平面上nnn张信用卡的中心点的位置和绕中心点逆时针旋转的弧度,求这nnn张信用卡的凸包。

方法:

因为有圆,圆的凸包是很难求的,所以先考虑点的凸包,在这里,比较特殊的点就是圆心,那么圆心的凸包是否和要求的凸包有一定联系?实际上要求的凸包长就是圆心的凸包长加以rrr为半径的圆的周长,下面给出一个证明。

(1)证明:任意凸nnn边形的内角和是180(n−2)°180(n-2)°180(n2)°

我们选取一个点为起始点向别的点连线分割三角形,显然邻近两点无法分割出,从任意方向的第三个点开始,每次与点的连线都可以分割出一个三角形,三角形总数是n−2n-2n2,所以得证。

(2)证明:任意凸nnn边形的的外交和为360°360°360°

每个外角即内角的补角,所以和为∑i=1n180−αi=180n−180(n−2)=360,αi\sum_{i=1}^{n}180-\alpha_{i}=180n-180(n-2)=360,\alpha_{i}i=1n180αi=180n180(n2)=360αi为其中一个内角

现在开始证明:

在这里插入图片描述

由于相切,那么∠OBC=∠OAC=180°→∠BOA+∠BCA=180°\angle OBC=\angle OAC=180°\rightarrow\angle BOA+\angle BCA=180°OBC=OAC=180°BOA+BCA=180°

设每个圆的这样的圆心角为α\alphaα,对应的多边形的内角为β\betaβ

那么∑i=1nαi+βi=180n\sum_{i=1}^{n}{\alpha_{i}+\beta_{i}}=180ni=1nαi+βi=180n

nnn边形内角和为180(n−2)180(n-2)180(n2)

所以∑i=1nαi=180n−180(n−2)=360\sum_{i=1}^{n}\alpha_{i}=180n-180(n-2)=360i=1nαi=180n180(n2)=360

所以所有的圆弧加起来是一个圆的周长,得证。

#include<bits/stdc++.h>
#define ll long long
using namespace std;

const double eps=1e-10,pi=acos(-1);

int cmp(double a,double b)
{
	if(fabs(a-b)<eps) return 0;
	else if(a-b>eps) return 1;
	return -1;
}

struct vec
{
	double x,y;
	bool operator==(const vec& a) const {return (x-a.x)<eps&&(y-a.y)<eps;}
	vec operator+(const vec& a) const {return (vec){x+a.x,y+a.y};}
	vec operator-(const vec& a) const {return (vec){x-a.x,y-a.y};}
	vec operator*(const double& k) const {return (vec){x*k,y*k};}
	vec operator/(const double& k) const {return (vec){x/k,y/k};}
	double operator^(const vec& a) const {return x*a.y-y*a.x;}//获得叉积的模
	double operator*(const vec& a) const {return x*a.x+y*a.y;}
	vec operator*(const pair<int,int>&a) const {return (vec){x*a.first,y*a.first};}
	friend istream& operator>>(istream& o,const vec& a)
	{
		scanf("%lf%lf",&a.x,&a.y);
		return o;
	}
	friend ostream& operator<<(ostream& o,const vec& a)
	{
		printf("%.10lf %.10lf\n",a.x,a.y);
		return o;
	}
};

struct line
{
	vec base,a,b;
};

bool rui(const vec& a,const vec& b){return a*b>eps;}
bool zhi(const vec& a,const vec& b){return fabs(a*b)<eps;}
bool dun(const vec& a,const vec& b){return a*b<-eps;}
bool operator!=(const vec& a,const vec& b){return !(a==b);}
double S(const vec& a,const vec& b){return fabs(a^b)/2;}
vec operator*(const double& k,const vec &a){return (vec){k*a.x,k*a.y};}
vec operator*(const pair<int,int>&a,const vec& b){return b*a;}
double getlen(const vec& a){return sqrt(a.x*a.x+a.y*a.y);}
double getangle(const vec& a,const vec& b){return acos(a*b/(getlen(a)*getlen(b)));}
vec getvertical(const vec& a){return (vec){a.y,-a.x};}
vec getsingle(const vec& a){return a/getlen(a);}
vec rotate(const vec& a,double k){return (vec){a.x*cos(k)-a.y*sin(k),a.x*sin(k)+a.y*cos(k)};}
line trans(const vec& a,const vec& b){return (line){getsingle(b-a),a,b};}
line trans(double a,double b,double x,double y){return (line){getsingle((vec){x-a,y-b}),(vec){a,b},(vec){x,y}};}

int n,cnt;
vector<vec>v(40005),s(40005),p(5);
double a,b,r,len1,len2;

inline void solve(int k)
{
	double theta;
	vec mid; cin>>mid; scanf("%lf",&theta); 
	p[1]=(vec){len1,len2};p[2]=(vec){len1,-len2};
	p[3]=(vec){-len1,len2};p[4]=(vec){-len1,-len2};
	for(int i=4*(k-1)+1;i<=4*k;i++) v[i]=rotate(p[i-4*(k-1)],theta)+mid;
}

int main()
{
	scanf("%d%lf%lf%lf",&n,&b,&a,&r); 
	len1=a/2-r,len2=b/2-r;
	for(int i=1;i<=n;i++) solve(i); n<<=2;
	sort(v.begin()+1,v.begin()+1+n,[](const vec& temp1,const vec& temp2){
		if(cmp(temp1.x,temp2.x)==0) return cmp(temp1.y,temp2.y)<1;
		return cmp(temp1.x,temp2.x)<1;
	});
	sort(v.begin()+2,v.begin()+1+n,[](const vec& temp1,const vec& temp2){
		double k=(temp1-v[1])^(temp2-v[1]);
		if(cmp(k,0)==0) return cmp(getlen(temp1-v[1]),getlen(temp2-v[1]))<1;
		return cmp(k,0)==1;
	});
	for(int i=1;i<=n;i++) 
	{
		while(cnt>1&&cmp((s[cnt]-s[cnt-1])^(v[i]-s[cnt]),0)<1) cnt--;
		s[++cnt]=v[i];
	}
	double ans=2*pi*r;
	for(int i=1;i<=cnt;i++) ans+=getlen(s[i]-s[i%cnt+1]);
	printf("%.2lf",ans);
  	return 0;
	for(int i=1;i<=cnt;i++) ans+=getlen(s[i]-s[i%cnt+1]);
	printf("%.2lf",ans);
  	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值