• 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
    ", 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;
    }
    


    www.cnblogs.com/tenlee
  • 相关阅读:
    判断日期是否是法定节假日或者休息日
    linux版powershell安装教程(.net core版)
    两款【linux字符界面下】显示【菜单】,【选项】的powershell脚本模块介绍
    powershell中使用超大内存对象
    powershell脚本,命令行参数传值,并绑定变量的例子
    在docker容器中安装和使用,linux版的powershell
    powershell开源新闻及简介
    用powershell+excel行列转置三步走
    让powershell同时只能运行一个脚本(进程互斥例子)
    powershell玩转SQL SERVER所有版本
  • 原文地址:https://www.cnblogs.com/tenlee/p/4420119.html
Copyright © 2020-2023  润新知