洛谷-P9830 题解
思路分析
分析样例:
见红线,长宽各为 2,存在格点;黄线长 2 宽 3,没有格点。
考虑延长黄线使得长 4 宽 6,发现有格点。思考格点,如果长和宽都可以被分成
推论 1:对于
对推论 1 的证明:
若存在格点
,其坐标为 ,由于在同一直线上,斜率 相同,则有 ,即 。由于 为整数,则有 。 采用反证法,
时存在格点。 由于互质,
,假设 , 必然有因子 ,而实际上没有,所以 对该式没有贡献。即: 。 而
是线段上一点,有 ,则 一定不属于 ,与原有条件冲突,由此可证。
得出推论 1 后,我们就能判断两点之间是否有格点了。那么如何得出最短答案呢?
(图是随手画的,具体有的性质以下文所述为准。)
见上,假设
假设
推论 2:对于任意不合法的取点,必然可以在原线段取到更优的合法方案。
那么就有:
推论 2.1:最优方案必然是合法的。
对推论 2.1 的证明:
假设最优方案不合法,根据推论 2,则有更优方案可以更新,与原有条件冲突,由此可得。
以上是一个转折点的情况,那如果有多个转折点呢?
如图,多个转折点的情况是不需要考虑的,见图,由于三角形定则,红色线的长度小于另外两条边之和。换句话说:
推论 3:最优方案只转折一次或零次。
于是我们只要枚举一个点就可以了,如果在整个
代码实现
#define by_wanguan #include<iostream> #include<cmath> #include<algorithm> using namespace std; int t,n,m,y11,y2,y3,g; double dn,dm,nowm,ans,k; double dis(double a,double b,double x,double y){ return sqrt((x-a)*(x-a)+(y-b)*(y-b)); } void solve(){ k=(double)m/n;//斜率 if((g=__gcd(n,m))==1) {printf("%.15lf\n",dis(0,0,n,m)); return ;} dn=n,dm=m; for(int i=1;i<n;i++){ nowm=k*i; y11=(int)(nowm),y2=y11+1,y3=y11-1;//x坐标为i时附近的点的y坐标 if(__gcd(n-i,m-y11)==1&&__gcd(i,y11)==1&&abs(y11-k*i)>1e-10) //判断是否合法,abs()是在判断是否为原线段上的点 ans=min(ans,dis(i,y11,n,m)+dis(0,0,i,y11)); if(__gcd(n-i,m-y2)==1&&__gcd(i,y2)==1&&abs(y2-k*i)>1e-10) ans=min(ans,dis(i,y2,n,m)+dis(0,0,i,y2)); if(__gcd(n-i,m-y3)==1&&__gcd(i,y3)==1&&abs(y3-k*i)>1e-10) ans=min(ans,dis(i,y3,n,m)+dis(0,0,i,y3)); } printf("%.15lf\n",ans); } int main(){ scanf("%d",&t); while(t--){ scanf("%d%d",&n,&m); ans=1e9; solve(); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步