Codeforces Gym-101617F Move Away [计算几何]

题意:给你n个圆,求包含在所有圆的区域内距离原点最远的点。

题解:这个点必定是交点或者是圆心与原点的直线与圆的交点。

AC代码:

#include<stdio.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define eps 1e-8
using namespace std;
struct point
{
    double x,y;
    point(){}
	point(double x,double y)
	{
		this->x=x;
		this->y=y;
	} 
};
double max(double a,double b){return a>b?a:b;}
double min(double a,double b){return a<b?a:b;}
point intersection(point u1,point u2,point v1,point v2)
{
    point ret=u1;
    double t=((u1.x-v1.x)*(v1.y-v2.y)-(u1.y-v1.y)*(v1.x-v2.x))
             /((u1.x-u2.x)*(v1.y-v2.y)-(u1.y-u2.y)*(v1.x-v2.x));
    ret.x+=(u2.x-u1.x)*t;
    ret.y+=(u2.y-u1.y)*t;
    return ret;
}
double distance(point p1,point p2)
{
    return sqrt((p1.x-p2.x)*(p1.x-p2.x)+(p1.y-p2.y)*(p1.y-p2.y));
}

void intersection_line_circle(point c,double r,point l1,point l2,point& p1,point& p2)
{
    point p=c;
    double t;
    p.x+=l1.y-l2.y;
    p.y+=l2.x-l1.x;
    p=intersection(p,c,l1,l2);
    t=sqrt(r*r-distance(p,c)*distance(p,c))/distance(l1,l2);
    p1.x=p.x+(l2.x-l1.x)*t;
    p1.y=p.y+(l2.y-l1.y)*t;
    p2.x=p.x-(l2.x-l1.x)*t;
    p2.y=p.y-(l2.y-l1.y)*t;
}

void intersection_circle_circle(point c1,double r1,point c2,double r2,point& p1,point& p2)
{
    point u,v;
    double t;
    t=(1+(r1*r1-r2*r2)/distance(c1,c2)/distance(c1,c2))/2;
    u.x=c1.x+(c2.x-c1.x)*t;
    u.y=c1.y+(c2.y-c1.y)*t;
    v.x=u.x+c1.y-c2.y;
    v.y=u.y-c1.x+c2.x;
    intersection_line_circle(c1,r1,u,v,p1,p2);
}
struct circle
{
	double r;
	point p;
}cir[55];
int mark[55],n;
bool judge(point p)
{
	for(int i=0;i<n;i++)
	{
		double dist=distance(cir[i].p,p);
		if(dist<cir[i].r+eps)continue;
		else return false;
	}
	return true;
}
int main()
{
	scanf("%d",&n);
	for(int i=0;i<n;i++)
		scanf("%lf%lf%lf",&cir[i].p.x,&cir[i].p.y,&cir[i].r);
	int sum=0;
	for(int i=0;i<n;i++)
		for(int j=i+1;j<n;j++)
		{
			double dist=distance(cir[i].p,cir[j].p);
			if(dist+min(cir[i].r,cir[j].r)<max(cir[i].r,cir[j].r)+eps)
			{
				if(cir[i].r>cir[j].r)mark[i]=1;
				else mark[j]=1;
			}
		}
	for(int i=0;i<n;i++)sum+=mark[i];
	if(sum==n-1)
	{
		for(int i=0;i<n;i++)
		{
			if(mark[i]==0)
				printf("%.3lf\n",distance(point(0.0,0.0),cir[i].p)+cir[i].r);
		}
		return 0;
	}
	double ans=0;
	for(int i=0;i<n;i++)
	{
		point p1,p2;
		for(int j=0;j<n;j++)
		{
			if(i==j||mark[i]||mark[j])continue;
			intersection_circle_circle(cir[i].p,cir[i].r,cir[j].p,cir[j].r,p1,p2);
			if(judge(p1)) ans=max(ans,distance(p1,point(0,0)));
			if(judge(p2)) ans=max(ans,distance(p2,point(0,0)));
		}
		intersection_line_circle(cir[i].p,cir[i].r,point(-cir[i].p.x,-cir[i].p.y),point(2000.0,2000.0*(cir[i].p.y/cir[i].p.x)),p1,p2);
		if(judge(p1)) ans=max(ans,distance(p1,point(0,0)));
		if(judge(p2)) ans=max(ans,distance(p2,point(0,0)));
	}
	printf("%.3f\n",ans);
} 

CodeForces - 616D是一个关于找到一个序列中最长的第k好子段的起始位置和结束位置的问题。给定一个长度为n的序列和一个整数k,需要找到一个子段,该子段中不超过k个不同的数字。题目要求输出这个序列最长的第k好子段的起始位置和终止位置。 解决这个问题的方法有两种。第一种方法是使用尺取算法,通过维护一个滑动窗口来记录\[l,r\]中不同数的个数。每次如果这个数小于k,就将r向右移动一位;如果已经大于k,则将l向右移动一位,直到个数不大于k。每次更新完r之后,判断r-l+1是否比已有答案更优来更新答案。这种方法的时间复杂度为O(n)。 第二种方法是使用枚举r和双指针的方法。通过维护一个最小的l,满足\[l,r\]最多只有k种数。使用一个map来判断数的种类。遍历序列,如果当前数字在map中不存在,则将种类数sum加一;如果sum大于k,则将l向右移动一位,直到sum不大于k。每次更新完r之后,判断i-l+1是否大于等于y-x+1来更新答案。这种方法的时间复杂度为O(n)。 以上是两种解决CodeForces - 616D问题的方法。具体的代码实现可以参考引用\[1\]和引用\[2\]中的代码。 #### 引用[.reference_title] - *1* [CodeForces 616 D. Longest k-Good Segment(尺取)](https://blog.csdn.net/V5ZSQ/article/details/50750827)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces616 D. Longest k-Good Segment(双指针+map)](https://blog.csdn.net/weixin_44178736/article/details/114328999)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值