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\n",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\n",ans);
    }while(dis(l,r)>eps);
    printf("%0.2lf",ans);
    return 0;
}

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

细节决定成败!

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

原文地址:https://www.cnblogs.com/TianMeng-hyl/p/12309214.html

时间: 2024-08-02 02:45:17

P2571 [SCOI2010]传送带——hyl天梦的相关文章

P2571 [SCOI2010]传送带

题意:在一个2维平面上有两条传送带,每一条传送带可以看成是一条线段. 两条传送带分别为线段AB和线段CD.lxhgww在AB上的移动速度为P, 在CD上的移动速度为Q,在平面上的移动速度R.现在lxhgww想从A点走到D点, 他想知道最少需要走多长时间 这貌似是物理题 第一道三分题,还是三分套三分(厉害了~~) 对AB,CD分别三分,求距离更新答案 因为判不了大小关系,取cnt=50来多次三分(足够了) #include<cstdio> #include<iostream> #in

bzoj 1857: [Scoi2010]传送带 三分

题目链接 1857: [Scoi2010]传送带 Time Limit: 1 Sec  Memory Limit: 64 MBSubmit: 934  Solved: 501[Submit][Status][Discuss] Description 在一个2维平面上有两条传送带,每一条传送带可以看成是一条线段.两条传送带分别为线段AB和线段CD.lxhgww在AB上的移动速度为P,在CD上的移动速度为Q,在平面上的移动速度R.现在lxhgww想从A点走到D点,他想知道最少需要走多长时间 Inpu

【BZOJ 1857】 [Scoi2010]传送带

1857: [Scoi2010]传送带 Time Limit: 1 Sec Memory Limit: 64 MB Submit: 737 Solved: 387 [Submit][Status][Discuss] Description 在一个2维平面上有两条传送带,每一条传送带可以看成是一条线段.两条传送带分别为线段AB和线段CD.lxhgww在AB上的移动速度为P,在CD上的移动速度为Q,在平面上的移动速度R.现在lxhgww想从A点走到D点,他想知道最少需要走多长时间 Input 输入数

bzoj1857: [Scoi2010]传送带

Description 在一个2维平面上有两条传送带,每一条传送带可以看成是一条线段.两条传送带分别为线段AB和线段CD.lxhgww在AB上的移动速度为P,在CD上的移动速度为Q,在平面上的移动速度R.现在lxhgww想从A点走到D点,他想知道最少需要走多长时间 Input 输入数据第一行是4个整数,表示A和B的坐标,分别为Ax,Ay,Bx,By 第二行是4个整数,表示C和D的坐标,分别为Cx,Cy,Dx,Dy 第三行是3个整数,分别是P,Q,R Output 输出数据为一行,表示lxhgww

BZOJ1857:[Scoi2010]传送带——题解

http://www.lydsy.com/JudgeOnline/problem.php?id=1857 Description 在一个2维平面上有两条传送带,每一条传送带可以看成是一条线段.两条传送带分别为线段AB和线段CD.lxhgww在AB上的移动速度为P,在CD上的移动速度为Q,在平面上的移动速度R.现在lxhgww想从A点走到D点,他想知道最少需要走多长时间 Input 输入数据第一行是4个整数,表示A和B的坐标,分别为Ax,Ay,Bx,By 第二行是4个整数,表示C和D的坐标,分别为

[SCOI2010]传送带

Description 在一个2维平面上有两条传送带,每一条传送带可以看成是一条线段.两条传送带分别为线段AB和线段CD.lxhgww在AB上的移动速度为P,在CD上的移动速度为Q,在平面上的移动速度R.现在lxhgww想从A点走到D点,他想知道最少需要走多长时间 Input 输入数据第一行是4个整数,表示A和B的坐标,分别为Ax,Ay,Bx,By 第二行是4个整数,表示C和D的坐标,分别为Cx,Cy,Dx,Dy 第三行是3个整数,分别是P,Q,R Output 输出数据为一行,表示lxhgww

UVA540 Team Queue——题解 by hyl天梦

UVA540 Team Queue 题解 题目描述:题目原题 https://vjudge.net/problem/UVA-540 Queues and Priority Queues are data structures which are known to most computer scientists. The Team Queue, however, is not so well known, though it occurs often in everyday life. At l

STL综合 容器常用集合 —— hyl天梦

STL 标准模板库 容器 用法集结 NO.1 stack 栈 注:取自https://www.cnblogs.com/aiguona/p/7200837.html 库 #include<stack> 定义方式 stack<_template> s;//参数也是数据类型,这是栈的定义方式 常用操作 s.empty()//如果栈为空返回true,否则返回false s.size()//返回栈中元素的个数 s.pop()//删除栈顶元素但不返回其值 s.top()//返回栈顶的元素,但不

2019年8月训练(壹)二分,三分

二分查找 P1024 一元三次方程求解 题目给出范围[-100,100],同时两根绝对值之差<=1,保证了每一个大小为1的区间里至多有1个解,也就是说当区间的两个端点的函数值异号时区间内一定有一个解,同号时一定没有解. 也就可以二分去做查找. AC码: #include<cstdio> #include<cstring> #include<algorithm> using namespace std; double a,b,c,d; double f(double