`

多边形可以容纳的半径最大的圆

阅读更多

/*
http://acm.hdu.edu.cn/showproblem.php?pid=3644
枚举,如果一个圆能放在多边形里面,你将这个圆移动,
直到跟多边形“相切",这个相切也不能算是标准的相切定义,
反正有三种情况:一、圆碰到了多边形的两个点;二、圆碰到
了多边形的一边一点;三、圆碰到了多边形的两边。以至于圆
不能在移动了,如此,你可以枚举这些情况,再判断圆是否放
得下。
*/

const double eps = 1e-8;
const double max = 1000000000.0; 
int sign(double d){ return d < -eps ? -1 : d > eps; }
struct point{
	double x, y;
	point(double x=0, double y=0) : x(x), y(y) {}
	void read(){ scanf("%lf%lf", &x, &y);}
	point operator-(point tp){ return point(x-tp.x, y-tp.y);  }
	point operator+(point tp){ return point(x+tp.x, y+tp.y);  }
};
inline double inter_pro(point st1, point ed1, point st2, point ed2){
	return (ed1.x-st1.x)*(ed2.y-st2.y) - (ed1.y-st1.y)*(ed2.x-st2.x);
}
inline double dist(point st, point ed){ return sqrt(pow(st.x-ed.x, 2.0) + pow(st.y-ed.y, 2.0)); }
inline bool isIntersect(point st1, point ed1, point st2, point ed2){  //判断线段是否相交,在端点处相交不算 ??
	if(inter_pro(st1, st2, st1, ed1) * inter_pro(st1, ed2, st1, ed1) >= 0) return false;
	if(inter_pro(st2, st1, st2, ed2) * inter_pro(st2, ed1, st2, ed2) >= 0) return false;
	return true;
}
inline bool isOnSegment(point st, point ed, point tp){ //判断点是否在线段上
	if(inter_pro(st, tp, st, ed) != 0) return false;
	if(fabs(dist(st, tp) + dist(ed, tp) - dist(st, ed)) < eps) return true;
	return false;
}
inline double distPtoL(point st, point ed, point tp){//点到线段的最近距离
	double d1, d2, d3;
	d1 = dist(st, ed); d2 = dist(st, tp); d3 = dist(ed, tp);
	if((d1*d1 + d3*d3 - d2*d2 < 0) || (d1*d1+d2*d2-d3*d3 < 0)) return min(d2, d3);
	return fabs(inter_pro(st, ed, st, tp)) / d1;
}
inline point getMid(point st, point ed){  //求线段的中点
	return point((st.x + ed.x) / 2.0, (st.y + ed.y) / 2.0);
}
inline point goLen(point st, point dir, double len){  //st沿着dir方向走len之后的点
	double tl = sqrt(dir.x * dir.x + dir.y * dir.y);
	if(sign(tl) == 0) return st;
	return point(st.x + len * dir.x / tl, st.y + len * dir.y / tl);
}

// (st->ed)的左侧为多边形的内侧
point change(point st, point ed, point next, double l){  //l为线段移动的长度
        //next为和返回的点相对应的点
	point dd;
	dd.x = -(ed - st).y;
	dd.y = (ed - st).x;
	double len = sqrt(dd.x * dd.x + dd.y * dd.y);
	dd.x /= len, dd.y /= len;
	dd.x *= l, dd.y *= l;
	dd = dd + next;
	return dd;
}
point intersectPoint(point st1, point ed1, point st2, point ed2){  //求相交直线的交点
	double t = inter_pro(st2, st1, st2, ed2) / ((ed1.y-st1.y) * (ed2.x-st2.x) - (ed1.x-st1.x) * (ed2.y-st2.y));
	return point(st1.x+(ed1.x-st1.x)*t, st1.y+(ed1.y-st1.y)*t);
}
point get_point(point st, point ed, point p){  //求p在直线(st,ed)上的垂点
	point ans;
	if(fabs(st.x-ed.x) < eps){
		ans.x = st.x;
		ans.y = p.y;
	}else if(fabs(st.y-ed.y) < eps){
		ans.x = p.x;
		ans.y = st.y;
	}else{
		double k1, d1, k2, d2;
		k1 = (ed.y - st.y) / (ed.x - st.x);
		d1 = st.y - st.x*(ed.y - st.y) / (ed.x - st.x);
		k2 = -1 / k1;
		d2 = p.y + p.x / k1;
		ans.x = (d2 - d1) * k1 / (k1*k1 + 1);
		ans.y = k1 * ans.x + d1;
	}
	return ans;
}
struct poly{
	static const int N = 10005; //点数目的最大值
	point ps[N];
	int pn;
	poly(){ pn = 0; }
	void push(point tp){ ps[pn++] = tp; }
	point& operator[](int k){ return ps[k];  }
	bool inPoly(point tp){  //判断点是否在多边形内
		int i;
		for(i = 0; i < 3; i++) ps[pn+i] = ps[i];
		for(i = 0; i < pn; i++){
			if(isOnSegment(ps[i], ps[i+1], tp)) return true;
		}
		int c = 0;  //c表示交点的个数
		point d1 = tp, d2;
		d2.y = d1.y;
		d2.x = maxx;   //构造一条足够长的线段,d2.x要根据数据的范围而定
		for(i = 0; i < pn; i++){
			if(isIntersect(d1, d2, ps[i], ps[i+1])
				||(isOnSegment(d1, d2, ps[i+1]) && (inter_pro(d1, d2, d1, ps[i]) *  inter_pro(d1, d2, d1, ps[i+2]) < 0))
				||(isOnSegment(d1, d2, ps[i+1]) && isOnSegment(d1, d2, ps[i+2]) && (inter_pro(d1, d2, d1, ps[i]) * inter_pro(d1, d2, d1, ps[i+3]) < 0)))
				c++;
		}
		return c % 2;
	}
	double getArea(){  //求面积
		ps[pn] = ps[0];
		int i;
		double ans;
		for(ans = i = 0; i < pn; i++) ans += ps[i].x * ps[i+1].y - ps[i].y * ps[i+1].x;
		return ans;
	}
	void reverse(){  //翻转
		int l = 0, r = pn - 1;
		point tp;
		while(l < r){ tp = ps[l]; ps[l] = ps[r]; ps[r] = tp;  l++, r--; }
	}
	bool isInnerCir(double R, point cen){  //判断半径为R,圆心为cen的圆是否在多边形内
		if(!inPoly(cen)) return false;
		ps[pn] = ps[0];
		int i;
		double dd;
		for(i = 0; i < pn; i++){ if(sign(distPtoL(ps[i], ps[i+1], cen) - R) < 0) return false; }
		return true; //圆在多边形内返回true
	}
	bool canHold(double R){  //判断该多边形是否可以容纳半径为R的圆
		if(sign(getArea()) < 0) reverse();
		ps[pn] = ps[0];
		int i, j;
		double d;
		point p1, p2, p3, p4, cen;
		for(i = 0; i < pn; i++){ //每举两个点
			for(j = i + 1; j < pn; j++){
				if(sign((d = dist(ps[i], ps[j])) - 2*R) <= 0){
					p1 = getMid(ps[i], ps[j]);
					p2 = ps[j] - ps[i];
					swap(p2.x, p2.y);  p2.x *= -1;   //向量逆时针旋转90度
					d = sqrt(R * R - d * d / 4.0);
					cen = goLen(p1, p2,d);
					if(isInnerCir(R, cen)) return true;
					p2.x *= -1; p2.y *= -1;  //反向
					cen = goLen(p1, p2,d);
					if(isInnerCir(R, cen)) return true;
				}
				//枚举线段和线段卡住圆的情况,把两条线段内移R的距离,将两条新的线段的交点作为圆心
				p1 = change(ps[i], ps[i+1], ps[i], R);
				p2 = change(ps[i], ps[i+1], ps[i+1], R);
				p3 = change(ps[j], ps[j+1], ps[j], R);
				p4 = change(ps[j], ps[j+1], ps[j+1], R);
				if(isIntersect(p1, p2, p3, p4)){
					cen = intersectPoint(p1, p2, p3, p4);
					if(isInnerCir(R, cen)) return true;
				}
				//枚举点和线段卡住圆的情况
				p1 = get_point(ps[i], ps[i+1], ps[j]);
				p2 = ps[i+1] - ps[i];
				if(sign(2 * R - dist(p1, ps[j])) < 0) continue;
				d = sqrt(R * R - (ps[j].x - p1.x) * (ps[j].y - p1.y));
				cen = goLen(p1, p2, d);
				if(isInnerCir(R, cen)) return true;
				p2.x *= -1; p2.y *= -1;
				cen = goLen(p1, p2, d);
				if(isInnerCir(R, cen)) return true;
			}
			int k;
		}
		return false;
	}
	double getMax(){ //计算多边形可以容纳的最大半径的圆
		double low, high, mid, step;
		low = 0;
		high = 10000;
		step = 0.001;
		while(low <= high){
			mid = (low + high) * 0.5;
			if(canHold(mid)) low = mid + step;
			else high = mid - step;
		}
		return low - step;
	}
};
 
0
1
分享到:
评论

相关推荐

    使用Matlab多边形的最大内切圆(最大圆)

    最大内切圆是指能够完全位于多边形内部且半径最大的圆。这个问题在计算机图形学、几何算法和地理信息系统等领域有广泛应用,比如在碰撞检测、地图分析等方面。 首先,我们来看“227-61.mat”、“001-43.mat”、...

    六年级数学下册第五章基本平面图形5多边形和圆的初步认识课件鲁教版五四制20200327152

    在实际问题中,比如地球赤道的例子,可以应用这些概念来解决实际问题,如计算两个圆的半径差以及判断两个圆之间的空间是否足够容纳物体。 最后,课程涵盖了与圆周长和面积相关的比例问题。如果一个圆的周长是另一个...

    九年级上数学讲义五-圆与圆的位置关系-综合能力提高题.doc

    15. 外公切线与连心线夹角:外公切线与连心线夹角已知,结合半径可以求出相关线段的长度。 16. 正方形与圆的关系:正方形的顶点在大圆上,小圆与正方形的边相切,利用正方形的性质和圆的性质可以找出AB的长度。 17...

    半平面相关题解1

    可以采用二分查找的方法,每次尝试一个半径r,如果该半径的圆与多边形无交,则增大r,否则减小r,直到找到最大值。 最后,POJ3384需要放置两个尽可能覆盖面积大的圆形红地毯,且可以重叠。同样采用二分搜索和半平面...

    MFC画直线,圆,椭圆等等

    例如,要画一个以(50, 50)为中心,半径为50的圆,代码如下: ```cpp CRect rect(0, 0, 100, 100); // 定义一个包含圆的矩形 dc.Ellipse(rect); // 画圆 ``` 椭圆的绘制与画圆类似,也是通过`Ellipse()`函数,只...

    山东省龙口市诸由观镇诸由中学2014-2015学年六年级数学上册 1.1 生活中的立体图形习题 鲁教版五四制

    - 面的形状可以是多边形或圆形。 - 棱连接顶点,形成几何体的骨架。 - 旋转一定角度,某些二维图形可以形成三维几何体。 8. **图形旋转**: - 图形绕虚线旋转一周可形成不同的立体图形,如矩形绕一边旋转一周...

    js box2d实例

    3. **形状(Shape)**:形状定义刚体的外观,可以是b2CircleShape(圆形)、b2PolygonShape(多边形)等。例如,创建一个圆形形状: ```javascript var circleShape = new b2CircleShape(1); // 半径为1 body....

    基于Matlab的U形滑道曲线的设计.pdf

    同时,U形链的上下两段圆弧半径不同,上圆弧半径小于下圆弧半径,以适应链节弯曲变化的需要,使得整个链式输送机构能够更加平稳地运行。 在文章的引言部分,还提到了天津市自然科学基金重点项目,该项目的名称为...

    2021届高三数学新高考“8+4+4”小题狂练(43)(解析).doc

    当有多个个体需要分配到不同位置,且每个位置可以容纳多个个体时,需要运用排列组合知识和计数原理(如分类计数原理)来计算不同的分配方式。这里主要考察了分类讨论的思想和组合计数技巧。 5. **函数单调性与奇偶...

    CAD2007快捷键

    3. C - 绘制圆 4. I - 插入块 5. B - 创建块 6. H - 图案填充 7. D - 标注样式管理器 8. E - 删除对象 9. F - 圆角 10. G - 群组对象 11. M - 移动物体 12. O - 偏移 13. P - 平移 14. S - 拉伸 15. W - 外部块 16. ...

    OSG下的ODE物理引擎架构

    这可以是球、盒子、多边形或者其他复杂的形状。然后,将这些几何体附加到对应的刚体上。 4. **设定碰撞空间**:碰撞空间是ODE中用于检测物体间碰撞的数据结构。可以选择简单的边界盒或更复杂的树结构,根据场景的...

Global site tag (gtag.js) - Google Analytics