zoukankan      html  css  js  c++  java
  • P2571 [SCOI2010]传送带——hyl天梦

    P2571 [SCOI2010]传送带题解————天梦

    如写的不好,请多见谅。

    对于这道题,我首先想说,确实困惑了我好久,看网上的各种题解,却都不尽人意,思路早已明白,却不会操作。最后想想,还是觉得自己试着写一个吧。一种思路,与题解的思路不同,但理论上可行,但我当时似乎也不太相信那所谓的“理论”,毕竟自己错过许多次,即使这样仍要相信自己吗?想着,便已经翻到了我所需要的——与自己思路相同的题解。网址是https://blog.csdn.net/qq_42920122/article/details/88622782,什么思路呢?,在这之前,我建议大家,一定要相信自己,接下来让我们一起往下看。

    题目

    参考网址https://www.luogu.com.cn/problem/P2571

    引入

    现在请各位读者先抛开这个题,先想这样一个问题,如果在一个平面上,有一个点E和一条线段CD,一般的,如何求得点到线段得最短距离?
    这个问题很简单,答案:垂线段最短。所以,如果我们在垂足两侧取点,所得到的答案肯定比垂线段大。如果有一个动点P,从端点C运动到端点D,设在平面直角坐标系中,x为PC的长度,y为PE的长度,那么这个函数是一定是一个形如谷底(山峰)的图像。我们都知道,有单调性时,可以用二分。极值怎么办?答案是三分。
    如果不懂三分,请参考其它博客。推荐https://www.cnblogs.com/newpanderking/archive/2011/08/25/2153777.html 我用的三分并不规范。

    思路

    有一个小事情大家可以理解一下。在题中,最优解实际上满足在AB上取一点E,在CD上取一点F作为拐点,即AE+EF+FD,为什么?读者可以自己画一画,即使你取两个不在线段上的点,也会有两个在直线上的点可以比原先点更优。

    好了,问题来了,两条直线怎么办?如果我们已经确定点E,那么,根据“引入”,我们就可以确定答案了,但是我们并不知道,怎么办?答案:三分AB就可以了。详细看代码。

    代码

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #include<sstream>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    #include<deque>
    #define dd double
    #define ll long long
    #define N 10000100
    using namespace std;
    
    dd ans=N;
    
    struct point{
    	dd x,y;//存点的坐标
    };
    point a,b,c,d;
    int p,q,r;
    const dd eps=1e-8;//精度,我比较习惯于开1e-8;
    
    dd dis(point l,point r)
    {
    	return sqrt((l.x-r.x)*(l.x-r.x)+(l.y-r.y)*(l.y-r.y));
    }//求点l到点r的距离,勾股定理
    
    dd f(point mid2,point midmid)
    {
    	return dis(mid2,midmid)/r+dis(midmid,d)/q;
    }//这个函数是对于AB上的点mid2和在CD上的点midmid,求一下从mid到midmid和midmid到点d的“时间”;
    
    dd Three_CD(point mid2)//三分CD,mid2是已将选好的“E”
    {
    	dd zanans=N;
    	point l,r; l.x=c.x; l.y=c.y; r.x=d.x; r.y=d.y;
    	while(dis(l,r)>eps)
    	{
    		point mid,midmid;
    		mid.x=(l.x+r.x)/2.0; mid.y=(l.y+r.y)/2.0;
    		midmid.x=(mid.x+r.x)/2.0; midmid.y=(mid.y+r.y)/2.0;
    		if(f(mid2,mid)<f(mid2,midmid))
    		{
    			zanans=min(ans,f(mid2,mid));
    			r=midmid;
    		}
    		else
    		{
    			zanans=min(ans,f(mid2,midmid));
    			l=mid;
    		}
    	}
    	return zanans;
    }
    
    int main()
    {
    	cin>>a.x>>a.y>>b.x>>b.y>>c.x>>c.y>>d.x>>d.y>>p>>q>>r;
    	point l,r; l.x=a.x; l.y=a.y; r.x=b.x; r.y=b.y;
    	while(dis(l,r)>eps)//三分AB
    	{
    		point mid,midmid;
    		mid.x=(l.x+r.x)/2.0; mid.y=(l.y+r.y)/2.0;
    		midmid.x=(mid.x+r.x)/2.0; midmid.y=(mid.y+r.y)/2.0;
    		dd ans1=Three_CD(mid);
    		dd ans2=Three_CD(midmid);//ans1是以mid为E的最优解,ans2同理,看着两个值哪个最优。
    		//cout<<ans1<<" "<<ans2<<" ";
    		ans1=ans1+dis(a,mid)/p;
    		ans2=ans2+dis(a,midmid)/p;
    		//cout<<ans1<<" "<<ans2<<" ";
    		if(ans1<ans2)
    		{
    			ans=min(ans,ans1);
    			r=midmid;
    		}
    		else
    		{
    			ans=min(ans,ans2);
    			l=mid;
    		}//更新ans
    		//printf("%0.2lf
    ",ans); 
    	}
    	printf("%0.2lf",ans); 
    	return 0;
    }
    

    你以为这就完了,不,没有!

    坑点

    请大家仔细想想,如若A、B两点重合,会怎么样?ans将会是N!C、D重合是一个道理。do-while可以很好地解决这个问题。
    真正AC代码(无注释)

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    #include<sstream>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    #include<deque>
    #define dd double
    #define ll long long
    #define N 10000100
    using namespace std;
    
    dd ans=N;
    
    struct point{
    	dd x,y;
    };
    point a,b,c,d;
    int p,q,r;
    const dd eps=1e-8;
    
    dd dis(point l,point r)
    {
    	return sqrt((l.x-r.x)*(l.x-r.x)+(l.y-r.y)*(l.y-r.y));
    }
    
    dd f(point mid2,point midmid)
    {
    	return dis(mid2,midmid)/r+dis(midmid,d)/q;
    }
    
    dd Three_CD(point mid2)
    {
    	dd zanans=N;
    	point l,r; l.x=c.x; l.y=c.y; r.x=d.x; r.y=d.y;
    	do 
    	{
    		point mid,midmid;
    		mid.x=(l.x+r.x)/2.0; mid.y=(l.y+r.y)/2.0;
    		midmid.x=(mid.x+r.x)/2.0; midmid.y=(mid.y+r.y)/2.0;
    		if(f(mid2,mid)<f(mid2,midmid))
    		{
    			zanans=min(ans,f(mid2,mid));
    			r=midmid;
    		}
    		else
    		{
    			zanans=min(ans,f(mid2,midmid));
    			l=mid;
    		}
    	}
    	while(dis(l,r)>eps);
    	return zanans;
    }
    
    int main()
    {
    	cin>>a.x>>a.y>>b.x>>b.y>>c.x>>c.y>>d.x>>d.y>>p>>q>>r;
    	point l,r; l.x=a.x; l.y=a.y; r.x=b.x; r.y=b.y;
    	do
    	{
    		point mid,midmid;
    		mid.x=(l.x+r.x)/2.0; mid.y=(l.y+r.y)/2.0;
    		midmid.x=(mid.x+r.x)/2.0; midmid.y=(mid.y+r.y)/2.0;
    		dd ans1=Three_CD(mid);
    		dd ans2=Three_CD(midmid);
    		//cout<<ans1<<" "<<ans2<<" ";
    		ans1=ans1+dis(a,mid)/p;
    		ans2=ans2+dis(a,midmid)/p;
    		//cout<<ans1<<" "<<ans2<<" ";
    		if(ans1<ans2)
    		{
    			ans=min(ans,ans1);
    			r=midmid;
    		}
    		else
    		{
    			ans=min(ans,ans2);
    			l=mid;
    		}
    		//printf("%0.2lf
    ",ans); 
    	}while(dis(l,r)>eps);
    	printf("%0.2lf",ans); 
    	return 0;
    }
    

    好了到这里就结束了。坑点也把我也坑过,这提醒我们要多细想与思考,考虑其特殊性。我就很缺乏这一点。若如有哪里写的不好或写错,敬请各位看官在评论区提出意见。最后,送读者们(虽然并不多)一句话,虽然已经听过多次:

    细节决定成败!

    现今听来,仍是觉得荡气回肠,掷地有声!

  • 相关阅读:
    C陷阱与缺陷代码分析之第2章语法陷阱
    Linux tail命令
    spring利用扫描方式对bean的处理(对任何版本如何获取xml配置信息的处理)
    mysql 初识之日志文件篇
    JavaScript实现复制功能
    [置顶] Hibernate从入门到精通(七)多对一单向关联映射
    android操作通讯录的联系人
    数据结构读书笔记(三)(C语言)
    Nginx 日志分析
    [WARNING] Using platform encoding (GBK actually) to copy filtered resources, i.e. build is platform
  • 原文地址:https://www.cnblogs.com/TianMeng-hyl/p/12309214.html
Copyright © 2011-2022 走看看