题目集地址 16HK
16 HK补题单:B 平面几何 C 思维 J 字符串 K搜索
B doors
题目地址B doors
题目大意:如图Alex要进入通道找到Bob,问要想遇到Bob最Alex的最大半径是多少。
有两个长为l的门开着一定角度,宽为w的通道。
思路:模拟一下Alex路过的地方,找到在不同情况下圆心到周围障碍物的最短距离,即可。关键是要求出一个点到线段的距离。
关于直线和线段的距离公式参考计算几何之 点到直线的距离&点到线段的距离 代码模板与证明。
AC代码:
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<iostream>
using namespace std;
double R,L,W,Alpha,Beta;
const double pi=acos(-1.0),eps=1e-9,inf=10000.0;
struct Point
{
double x,y;
Point()
{
x=y=0;
}
Point(double _x,double _y)
{
x=_x,y=_y;
}
Point operator+(Point v)
{
return Point(x+v.x,y+v.y);
}
Point operator-(Point v)
{
return Point(x-v.x,y-v.y);
}
double operator*(Point v)
{
return x*v.x+y*v.y;
}
bool operator==(Point v)
{
if(x==v.x&&y==v.y)
return true;
else
return false;
}
double len()
{
return hypot(x,y);//计算直角三角形斜边长,其中两个参数为两个直角三角形的长度
}
};
double cross(Point a,Point b)//求向量叉乘
{
return a.x*b.y-a.y*b.x;
}
double dot(Point a,Point b)//求向量点积
{
return a.x*b.x+a.y*b.y;
}
double distance_line(Point p,Point a,Point b)
{
Point v1 = b - a,v2 = p - a;
return fabs(cross(v1,v2) / v1.len());//cross是v1和v2的叉积
}
double dist_point_to_segment(Point p,Point a,Point b)//点p到线段ab的距离
{
if(a == b)
return (p-a).len();
Point v1 = b - a,v2 = p - a,v3 = p - b;
if(dot(v1,v2) < 0) //dot是v1和v2的点积
return v2.len();
if(dot(v1,v3) > 0)
return v3.len();
return distance_line(p,a,b);
}
double cal(Point a,Point b,Point c,Point d)
{
return min(min(dist_point_to_segment(a,c,d),
dist_point_to_segment(b,c,d)),
min(dist_point_to_segment(c,a,b),
dist_point_to_segment(d,a,b)));
}
double solve()
{
Point A(-inf,W),B(0,W);
Point D(L,W),E(inf,W);
Point G(L,0),H(inf,0);
Alpha=pi-Alpha,Beta=pi-Beta;
Point C=D+Point(cos(Alpha)*L,sin(Alpha)*L);
Point F=G+Point(cos(Beta)*L,sin(Beta)*L);
double ans=min(L,W);
ans=min(ans,cal(A,B,C,D));
ans=min(ans,cal(C,D,F,G));
ans=min(ans,cal(D,E,F,G));
ans/=2.0;
ans=min(ans,R);
ans=max(ans,0.0);
return ans;
}
int main()
{
int t;
scanf("%lf%lf%lf",&R,&L,&W);
scanf("%d",&t);
while(t--)
{
scanf("%lf%lf",&Alpha,&Beta);
printf("%.9f\n",solve());
}
return 0;
}
C Playing with Numbers 数学
题目地址C Playing with Numbers
题目大意:给n组数字a,b,表示数字
2
a
3
b
2^a3^b
2a3b,执行两种操作gcd(X,Y),lcm(X,Y),输出n-1行,每一行表示执行i-1次gcd操作,n-i次lcm操作,先输出最大值的a和b,然后输出最小值的a和b
思路:
首先两个数的gcd的结果用a和b表示就是min(a)和min(b),两个数的lcm是max(a)和max(b)
第1行是只执行lcm操作,最后的结果只有一个,max(a)和max(b)。
对称的最后一行是只执行gcd操作,最后也只有一个结果,就是min(a)和min(b)。
第2行是只执行一次gcd操作,先看最小值,我们可以将所有数字中最小的值留下来,其他的数字做lcm之后,得到的是其他数字的maxa和maxb,然后在和最小数字的a和b各取最小值即可(这里我们当时没有深究,直接输出了最小的值得a和b就过了)
对应的倒数第2行只执行一次lcm操作,我们最后取最大值即可。
中间的情况就比较简单了,最大值就是maxa和maxb,最小值就是mina和minb
然后n等于1,2,3的时候需要特殊判断一下就可以了
AC代码:
/*
**
*/
#include <bits/stdc++.h>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int N = 5e4+10;
struct num{
int a,b;
}da[N];
int maxa,mina,maxb,minb;
bool com(num a,num b)
{
return (a.a*log(2)+a.b*log(3)<b.a*log(2)+b.b*log(3));
}
void solve()
{
int n;
scanf("%d",&n);
for(int i = 1;i <= n;i++)
{
scanf("%d%d",&(da[i].a),&(da[i].b));
}
sort(da+1,da+1+n,com);
maxa=mina=da[1].a;
maxb=minb=da[1].b;
for(int i = 1;i <= n;i++)
{
maxa = max(maxa,da[i].a);
mina = min(mina,da[i].a);
maxb = max(maxb,da[i].b);
minb = min(minb,da[i].b);
}
if(n==1)
printf("%d %d %d %d\n",maxa,maxb,maxa,maxb);
if(n==2)
{
printf("%d %d %d %d\n",maxa,maxb,maxa,maxb);
printf("%d %d %d %d\n",mina,minb,mina,minb);
}
if(n==3)
{
printf("%d %d %d %d\n",maxa,maxb,maxa,maxb);
printf("%d %d %d %d\n",da[n].a,da[n].b,da[1].a,da[1].b);
printf("%d %d %d %d\n",mina,minb,mina,minb);
}
if(n>=4)
{
printf("%d %d %d %d\n",maxa,maxb,maxa,maxb);
printf("%d %d %d %d\n",maxa,maxb,da[1].a,da[1].b);
for(int i = 3;i < n-1;i++)
{
printf("%d %d %d %d\n",maxa,maxb,mina,minb);
}
printf("%d %d %d %d\n",da[n].a,da[n].b,mina,minb);
printf("%d %d %d %d\n",mina,minb,mina,minb);
}
}
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int t = 1;
while(t--)
{
solve();
}
return 0;
}