POJ 1418 Viva Confetti(Japan 2002 Kanazawa)

点击打开链接

算法竞赛入门经典--训练指南

题目大意:n个圆盘依次放在桌面上,给出每个圆盘的坐标和圆心,求能看见的圆的个数;

分析:圆的每个可见部分由小圆弧围成,因此可以先求出所有小圆弧,然后判断每段小圆弧内外两侧的可见圆盘.具体来说,把小圆弧中点往内外两侧各移动很小距离,得到两个点,然后标记包含这两个点的圆盘中最顶部的那个为可见的;

算法实现:离散化求出与一个圆的所有交点的弧度,排序后, 两个相邻的交点之间 只有 一个 弧,在求这个弧的中点,分别向内外移动,判断

CODE:

#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstdio>
using namespace std;

const int  MAX_N = 128;
const double EPS = 5e-13;
const double PI = acos(-1.0);

typedef struct
{
	double x, y;
} point;

//两点距离
double Distance(const point & p1, const point & p2);

//如果一个角大于360,减去360,小于0加上360
double MainAngle(double a);

int  n;    //number of circles
point o[MAX_N];  //圆心
double r[MAX_N];  //圆的弧度

int  pan;   //与这个圆的交点数目
double pa[2 * MAX_N]; //存放与这个圆所有交点对应的弧度
int  visible[MAX_N]; //是否被访问过
int  ans;   //answer

int main()
{

	int i, j, k, t;
	point tp;
	double a, b, d;
	while (scanf("%d", &n), n)
	{
		for (i = 0; i < n; ++i)
		{
			scanf("%lf %lf %lf", &o[i].x, &o[i].y, &r[i]);
			visible[i] = 0;
		}

		for (i = 0; i < n; ++i)
		{
			pan = 0;
			pa[pan++] = 0;
			pa[pan++] = 2 * PI;

			for (j = 0; j < n; ++j)
			{
				if (j == i)
				{
					continue;
				}

				d = Distance(o[i], o[j]); //判断两个圆心距离

				if (r[i] + r[j] < d || r[i] + d < r[j] || r[j] + d < r[i]) //包含或不相交的
				{
					continue;
				}

				a = atan2(o[j].y - o[i].y, o[j].x - o[i].x);//atan2(),是求这个点和x轴正方形夹角,*PI/180  得到度数
				b = acos((r[i] * r[i] + d * d - r[j] * r[j]) / (2 * r[i] * d));

				pa[pan] = MainAngle(a + b);
				pan++;
				pa[pan] = MainAngle(a - b);
				pan++;
			}

			sort(pa, pa + pan);

			for (j = 0; j < pan - 1; ++j)
			{
				a = (pa[j] + pa[j + 1]) / 2;

				for (t = -1; t < 2; t += 2) //t = -1 或 1
				{
					//将每段圆弧中点往内外各移动很小距离
					tp.x = o[i].x + (r[i] + t * EPS) * cos(a);
					tp.y = o[i].y + (r[i] + t * EPS) * sin(a);

					for (k = n - 1; k >= 0; --k)
					{
						//如果找到第一个cover point i 的 Arc j 的圆,break
						if (Distance(tp, o[k]) < r[k])
						{
							break;
						}
					}
					visible[k] = 1;
				}
			}
		}

		ans = 0;

		for (i = 0; i < n; ++i)
			if (visible[i] == 1)
			{
				ans++;
			}

		printf("%d\n", ans);
	}

	return 0;
}



double Distance(const point & p1, const point & p2)
{
	return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y));
}



double MainAngle(double a)
{
	while (a > 2 * PI)
	{
		a -= 2 * PI;
	}
	while (a < 0)
	{
		a += 2 * PI;
	}

	return a;
}


转载于:https://www.cnblogs.com/tenlee/p/4420119.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值