信息学奥赛一本通 提高篇 提高版 第一部分 基础算法 第2章 二分与三分

信息学奥赛一本通 提高篇 提高版 第一部分 基础算法 第2章 二分与三分

//二分法,最经典的例子,就是求平方根。 
//2
//1.414
//3
//1.732
//程序执行效果如下,2018-10-15 21:04 

#include <stdio.h>
#define eps 1e-12
double sqrt(double x){
    double left=0,right=x,mid;
    while(left+eps<right){
        mid=(left+right)/2;
        if(mid*mid<x)left=mid;
        else right=mid;
    }
    return right;
}
int main(){
    double d;
    scanf("%lf",&d);
    printf("%lf\n",sqrt(d));
    return 0;

 

#10011 「一本通 1.2 例 1」愤怒的牛

//#10011 「一本通 1.2 例 1」愤怒的牛
//在线测评地址https://loj.ac/problem/10011 
//该题与 1247:河中跳房子 思路基本一致http://ybt.ssoier.cn:8088/problem_show.php?pid=1247在线测评地址 
//模拟如下,先排序1 2 4 8 9 left=0 right=9-1=8 mid=(left+right)/2=4
//x=4 剩1 8两个牛舍,太少故right=4 left=0 mid=(left+right)/2=2 
//x=2 剩1 4 8三个牛舍,不大于3个牛舍,left=2 right=4 mid=(left+right)/2=3 
//x=3 剩1 4 8三个牛舍,不大于3个牛舍,left=3 right=4 left+1<right 循环结束 
//样例通过,提交AC。2018-10-5 08:06 
#include <cstdio>
#include <algorithm>
using namespace std;
int a[100100],n,m;
int judge(int x){//牛舍数量过多或刚好left=mid 返回1 牛舍数量过少right=mid返回0
    int i,pre,cnt;//cnt统计剩下的牛舍数量 
    pre=1,cnt=1;//pre保存符合条件,最近选用的牛舍序号 
    for(i=2;i<=n;i++)
        if(a[i]-a[pre]>=x)
            cnt++,pre=i;
    if(cnt<m)return 0;
    return 1;
}
int main(){
    int i,left,right,mid;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)scanf("%d",&a[i]);
    sort(a+1,a+n+1);
    left=0,right=a[n]-a[1];
    while(left+1<right){
        mid=(left+right)/2;
        if(judge(mid))left=mid;
        else right=mid;
    }
    printf("%d\n",left);
}

#10012 「一本通 1.2 例 2」Best Cow Fences

//#10012. 「一本通 1.2 例 2」Best Cow Fences
//在线测评地址https://loj.ac/problem/10012
//在线测评地址https://vjudge.net/problem/POJ-2018

//样例理解如下:


//前缀和,想到了,用也仅能用在O(n^2)时间复杂度上 
//https://blog.csdn.net/Coldfresh/article/details/79617912此文注释写得棒,代码值得读
//通过本题,对二分有了更深的认识。2018-10-5 16:01
//样例通过,提交Wrong Answer. 
//修改,double min_left=1e10,ans=-1e10;//此处写成min_left=1e6,ans=-2e8 ,提交Wrong Answer.
//该题在提交过程中,最难的还是输出部分的处理。 printf("%d\n",(int)(right*1000));//此处写成printf("%.0lf\n",right*1000);//此处写成printf("%d\n",(int)(left*1000));//此处写成 printf("%.0lf\n",left*1000);
//修改,提交AC。2018-10-5 16:45 
#include <stdio.h>
#define maxn 100100
#define eps 1e-6
double a[maxn],b[maxn],c[maxn];
int n,L;
double min(double a,double b){
    return a<b?a:b;
}
double max(double a,double b){
    return a>b?a:b;
}
int judge(double x){
    int i;
    double min_left=1e10,ans=-1e10;//此处写成min_left=1e6,ans=-2e8//此处写成 ans=1e-6
    for(i=1;i<=n;i++)b[i]=a[i]-x;
    c[0]=0;
    for(i=1;i<=n;i++)c[i]=c[i-1]+b[i];
    for(i=L;i<=n;i++){
        min_left=min(min_left,c[i-L]);//计算最小的左端点 
        ans=max(ans,c[i]-min_left);//计算最大的和 
    }
    if(ans<0)return 0;//mid取得过大,right=mid
    else return 1;
}
int main(){
    int i;
    double left=1e6,right=-1e6,mid;
    scanf("%d%d",&n,&L);
    for(i=1;i<=n;i++){
        scanf("%lf",&a[i]);
        left=min(left,a[i]);
        right=max(right,a[i]);
    }
    while(left+eps<right){
        mid=(left+right)/2;
        if(judge(mid))
            left=mid;
        else 
            right=mid;
    }
    printf("%d\n",(int)(right*1000));//此处写成printf("%.0lf\n",right*1000);//此处写成printf("%d\n",(int)(left*1000));//此处写成 printf("%.0lf\n",left*1000);
    return 0;


#10013 「一本通 1.2 例 3」曲线

//#10013. 「一本通 1.2 例 3」曲线
//在线测评地址https://loj.ac/problem/10013
//在线测评地址https://vjudge.net/problem/UVA-1476
//在线测评地址https://vjudge.net/problem/HDU-3714
//题意确实没明白,实则样例没有弄懂。 
//https://blog.csdn.net/zuihoudebingwen/article/details/8037089该文说得不错,摘抄如下:
//读题读了好久,汗。。就是给你n个二次函数,定义域为[0,1000], 
//求x在定义域中每个x所在的n个函数的最大值的最小值。
//有二次函数的性质,很明显不的三分题。。。 
//为了帮助大家理解该题,特别绘制了第二组数据对应的图形,及输出结果0.5000的由来

//不了解 三分 的,可以通过此文了解三分。https://blog.csdn.net/pi9nc/article/details/9666627
//样例通过,提交Wrong Answer.
//查了半天,发现是精度问题, #define eps 1e-12//此处写成 #define eps 1e-6
//修改,提交AC。2018-10-6 12:48 
#include <stdio.h>
#define maxn 100100
#define eps 1e-12//此处写成 #define eps 1e-6
double a[maxn],b[maxn],c[maxn];
double max(double a,double b){
    return a>b?a:b;
}
double S(double a,double b,double c,double x){
    return a*x*x+b*x+c;
}
double F(int n,double x){
    int i;
    double ans=-1e10;
    for(i=1;i<=n;i++)
        ans=max(ans,S(a[i],b[i],c[i],x));
    return ans;
}
int main(){
    int T,n,i;
    double left,mid,mmid,right;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(i=1;i<=n;i++)
            scanf("%lf%lf%lf",&a[i],&b[i],&c[i]);
        left=0,right=1000;
        while(left+eps<right){
            mid=(left+right)/2,mmid=(mid+right)/2;
            if(F(n,mid)<F(n,mmid))
                right=mmid;
            else
                left=mid;
        }
        printf("%.4lf\n",F(n,left));
    }
    return 0;

 

#10014 「一本通 1.2 练习 1」数列分段 II

//#10014. 「一本通 1.2 练习 1」数列分段 II
//在线测评地址https://loj.ac/problem/10014
//在线测评地址https://www.luogu.org/problemnew/show/P1182
//样例模拟如下 
//4 2 4 5 1
//left=5 right=16 mid=10 [4 2 4][5 1] section=2 right=mid
//left=5 right=10 mid=7 [4 2][4][5 1] section=3 right=mid
//left=5 right=7 mid=6 [4 2][4][5 1] section=3 right=mid
//left=5 right=6 left+1<right 循环结束
//二分法,最后结果输出是left,还是right,模拟是关键
//样例通过,提交Wrong Answer.
//修改,int section=1,sum=0,i;//此处写成 section=0 一开始就是一段
//提交, https://www.luogu.org/problemnew/show/P1182中测试点4,WA。
//决定重新模拟样例
//4 2 4 5 1
//left=5 right=16 mid=10 [4 2 4][5 1] section=2 right=mid
//left=5 right=10 mid=7 [4 2][4][5 1] section=3 right=mid
//left=5 right=7 mid=6 [4 2][4][5 1] section=3 right=mid
//for(i=left;i<=right;i++)//一直被选left还是right所困扰,今日学得一招,真是管用2018-10-6 19:57
//https://www.luogu.org/discuss/show?postid=17784参考此文代码,习得上述技巧。 
//提交,AC。2018-10-6 19:57 
//https://loj.ac/problem/10014提交,AC。2018-10-6 20:10 
#include <stdio.h>
int n,m,a[1000100];
int max(int a,int b){
    return a>b?a:b;
}
int judge(int x){//section<=m right=mid 1否则 left=mid 0
    int section=1,sum=0,i;//此处写成 section=0 一开始就是一段 
    for(i=1;i<=n;i++){
        sum+=a[i];
        if(sum>x){
            sum=a[i];
            section++;
        }
    }
    if(section<=m)return 1;
    else return 0;
}
int main(){
    int i,left=-1,right=0,mid;
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++){
        scanf("%d",&a[i]);
        left=max(left,a[i]);
        right+=a[i];
    }
    while(left+1<right){
        mid=(left+right)/2;
        if(judge(mid))right=mid;
        else left=mid;
    }
    //此处写成printf("%d\n",right);
    for(i=left;i<=right;i++)//一直被选left还是right所困扰,今日学得一招,真是管用2018-10-6 19:57 
        if(judge(i)){
            left=i;
            break;
        }
    printf("%d\n",left);
    return 0;


#10015 「一本通 1.2 练习 2」扩散

方法一:Floyd算法

//#10015. 「一本通 1.2 练习 2」扩散
//在线测评地址https://loj.ac/problem/10015
//在线测评地址https://www.luogu.org/problemnew/show/P1661
//样例执行过程,如下图:

//((5-0)+(5-0)+1)/2=5

//https://baike.baidu.com/item/%E6%9B%BC%E5%93%88%E9%A1%BF%E8%B7%9D%E7%A6%BB/743092?fr=aladdin了解了曼哈顿距离 
//试着举几个例子,找找规律 
//输入: 
//2
//0 0
//1 2
//输出: 
//2
//样例执行过程如下:


//((1-0)+(2-0)+1)/2=2
//两个例子的计算中,均出现了曼哈顿距离。+1是为了适应奇数距离,/2是因为两个点同时增长。2018-10-7 19:40

//方法一:采用Floyd算法,将各点之间最短距离算一遍;
//第二步,在各点间最短距离中找最大距离,即为答案。
//看了,该题,x,y的数据范围,感觉采用int是在刀尖上行走,决定采用long long 
//样例通过,提交WA。
//发现,将Floyd的循环顺序写错,for(k=1;k<=n;k++)//以k点为中介,进行查找//将该循环写到了最内层。修改,提交WA。 
//发现,if(map[i][j]!=((LL)1<<33)&&ans<map[i][j])//此处写成 if(ans<map[i][j]) 修改,提交WA。
//参考了,https://www.luogu.org/problemnew/solution/P1661 
//弄到最后,还是发现,i,j,k三个点时,算法出错了,
//map[i][j]=min(map[i][j],max(map[i][k],map[k][j]));//终归还是要画图模拟//此句写成 if(map[i][j]>map[i][k]+map[k][j])map[i][j]=map[i][k]+map[k][j];
//修改,提交AC。2018-10-7 21:10 
#include <stdio.h>
#define LL long long
struct node{
    LL x,y;
}point[55];
LL n,map[55][55];
LL abs(LL a){
    if(a<0)a=-a;
    return a;
}
LL getDist(LL i,LL j){
    return (abs(point[i].x-point[j].x)+abs(point[i].y-point[j].y)+1)/2;
}
LL min(LL a,LL b){
    return a<b?a:b;
}
LL max(LL a,LL b){
    return a>b?a:b;
}
int main(){
    LL i,j,k,ans=-1;
    scanf("%lld",&n);
    for(i=1;i<=n;i++)
        scanf("%lld%lld",&point[i].x,&point[i].y);
    for(i=1;i<=n;i++)//初始化邻接矩阵 
        for(j=1;j<=n;j++)
            if(i==j)map[i][j]=0;
    for(i=1;i<=n;i++)//初始化邻接矩阵 
        for(j=i+1;j<=n;j++)
            map[j][i]=map[i][j]=getDist(i,j);
    for(k=1;k<=n;k++)//以k点为中介,进行查找//将该循环写到了最内层。 
        for(i=1;i<=n;i++)//Floyd算法 
            for(j=1;j<=n;j++)
                map[i][j]=min(map[i][j],max(map[i][k],map[k][j]));//终归还是要画图模拟//此句写成 if(map[i][j]>map[i][k]+map[k][j])map[i][j]=map[i][k]+map[k][j];
    for(i=1;i<=n;i++)//找两点间最大距离 
        for(j=i+1;j<=n;j++)
            if(ans<map[i][j])//此处写成 if(ans<map[i][j])
                ans=map[i][j];
    printf("%lld\n",ans);
    return 0;
}

//#10015. 「一本通 1.2 练习 2」扩散
//方法二:Kruskal算法,最小生成树中找最长的边
//样例通过,提交AC。2018-10-8 16:55
//从编程思路及成功率来说,方法二远胜方法一。
#include <cstdio>
#include <algorithm>
#define LL long long
#define maxn 55
LL f[maxn],n,x[maxn],y[maxn],e_cnt=0;
using namespace std;
struct node{
    LL i,j;
    LL w;
}e[maxn*maxn/2];
LL myAbs(LL a){
    if(a<0)a=-a;
    return a;
}
LL getDist(LL i,LL j){
    return (myAbs(x[i]-x[j])+myAbs(y[i]-y[j])+1)/2;
}
LL cmp(const node &a,const node &b){
    return a.w<b.w;
}
LL getFather(LL u){
    if(f[u]==u)return u;
    return f[u]=getFather(f[u]);
}
LL merge(LL u,LL v){
    LL f1=getFather(u),f2=getFather(v);
    if(f1==f2)return 0;
    f[f2]=f1;//左靠
    return 1;
}
int main(){
    LL i,j,cnt=0;
    scanf("%lld",&n);
    for(i=1;i<=n;i++)
        scanf("%lld%lld",&x[i],&y[i]);
    for(i=1;i<=n;i++)f[i]=i;
    for(i=1;i<=n;i++)
        for(j=i+1;j<=n;j++){
            e_cnt++;
            e[e_cnt].i=i,e[e_cnt].j=j;
            e[e_cnt].w=getDist(i,j);
        }
    sort(e+1,e+1+e_cnt,cmp);
    for(i=1;i<=e_cnt;i++){
        if(merge(e[i].i,e[i].j)){
            cnt++;
            if(cnt==n-1){
                printf("%lld\n",e[i].w);
                break;
            }
        }
    }
    return 0;
}

//P1661 扩散
//方法三:二分+图的遍历
//图的遍历,图的存储若用邻接矩阵,算法的时间复杂度过高,
//故采用邻接表的方式存储。
//样例通过,提交,https://www.luogu.org/problemnew/show/P1661测试点4,5 RE。
//将 #define maxn 55改成 #define maxn 550,提交AC。
//继续查,查了半天,发现是 e[maxn*maxn]的问题,写成 e[maxn*maxn*10]提交AC。
//不明个中原因,自个造了一组数据,跟踪,才发现,

50

1    1
2    2
3    3
4    4
5    5
6    6
7    7
8    8
9    9
10    10
11    11
12    12
13    13
14    14
15    15
16    16
17    17
18    18
19    19
20    20
21    21
22    22
23    23
24    24
25    25
26    26
27    27
28    28
29    29
30    30
31    31
32    32
33    33
34    34
35    35
36    36
37    37
38    38
39    39
40    40
41    41
42    42
43    43
44    44
45    45
46    46
47    47
48    48
49    49
50    50

//cnt每次也要初始化,没考虑到这个问题,才导致数组越界。
//提交AC。该题,初始化十分关键。2018-10-9
//方法三 优于 方法一
//最优的方法是 方法二
#include <stdio.h>
#include <string.h>
#define maxn 55
#define LL long long
LL head[maxn],cnt,n,x[maxn],y[maxn],map[maxn][maxn],vis[maxn];//此处写成 cnt=0
struct node{
    LL to;//边对应的点
    LL next;//下一条边
}e[maxn*maxn];
LL min(LL a,LL b){
    return a<b?a:b;
}
LL max(LL a,LL b){
    return a>b?a:b;
}
void addEdge(LL u,LL v){
    cnt++,e[cnt].to=v,e[cnt].next=head[u],head[u]=cnt;
}
LL myAbs(LL a){
    if(a<0)a=-a;
    return a;
}
LL getDist(LL i,LL j){
    return (myAbs(x[i]-x[j])+myAbs(y[i]-y[j])+1)/2;
}
LL judge(LL x){
    LL i,j,b,q[maxn],h,t,u,v;
    cnt=0;//漏了此句
    memset(vis,0,sizeof(vis)),memset(head,0,sizeof(head));
    for(i=1;i<=n;i++)//将邻接矩阵 转成 邻接表
        for(j=i+1;j<=n;j++)
            if(map[i][j]<=x)
                addEdge(i,j),addEdge(j,i);
    h=t=1,q[t]=1,vis[1]=1,t++;//广度优先遍历
    while(h<t){
        u=q[h];
        b=head[u];
        while(b){
            v=e[b].to;
            if(vis[v]==0)
                q[t]=v,vis[v]=1,t++;
            b=e[b].next;
        }
        h++;
    }
    for(i=1;i<=n;i++)
        if(vis[i]==0)return 0;//距离太小
    return 1;//距离太长或刚好。
}
int main(){
    LL i,j,left=2000000000,right=-1,mid;//p_cnt点的个数
    memset(map,0,sizeof(map));
    scanf("%lld",&n);
    for(i=1;i<=n;i++)scanf("%lld%lld",&x[i],&y[i]);
    for(i=1;i<=n;i++)
        for(j=i+1;j<=n;j++)
            map[j][i]=map[i][j]=getDist(i,j);
    for(i=1;i<=n;i++)
        for(j=i+1;j<=n;j++){
            left=min(left,map[i][j]);
            right=max(right,map[i][j]);
        }
    while(left+1<right){
        mid=(left+right)/2;
        if(judge(mid))right=mid;
        else left=mid;
    }
    printf("%lld\n",right);
    return 0;
}

//方法四:二分+图的遍历
//图的遍历采用 深度优先遍历 
//样例通过,提交AC。2018-10-9 21:11 
#include <stdio.h>
#include <string.h>
#define maxn 55
#define LL long long
LL head[maxn],cnt,n,x[maxn],y[maxn],map[maxn][maxn],vis[maxn];//此处写成 cnt=0
struct node{
    LL to;//边对应的点
    LL next;//下一条边
}e[maxn*maxn];
LL min(LL a,LL b){
    return a<b?a:b;
}
LL max(LL a,LL b){
    return a>b?a:b;
}
void addEdge(LL u,LL v){
    cnt++,e[cnt].to=v,e[cnt].next=head[u],head[u]=cnt;
}
LL myAbs(LL a){
    if(a<0)a=-a;
    return a;
}
LL getDist(LL i,LL j){
    return (myAbs(x[i]-x[j])+myAbs(y[i]-y[j])+1)/2;
}
void dfs(LL u){
    LL b,v;
    b=head[u];
    while(b){
        v=e[b].to;
        if(vis[v]==0){
            vis[v]=1;
            dfs(v);
        }
        b=e[b].next;
    }

LL judge(LL x){
    LL i,j;
    cnt=0;//漏了此句
    memset(vis,0,sizeof(vis)),memset(head,0,sizeof(head));
    for(i=1;i<=n;i++)//将邻接矩阵 转成 邻接表
        for(j=i+1;j<=n;j++)
            if(map[i][j]<=x)
                addEdge(i,j),addEdge(j,i);
    vis[1]=1;//深度优先遍历
    dfs(1);
    for(i=1;i<=n;i++)
        if(vis[i]==0)return 0;//距离太小
    return 1;//距离太长或刚好。
}
int main(){
    LL i,j,left=2000000000,right=-1,mid;//p_cnt点的个数
    memset(map,0,sizeof(map));
    scanf("%lld",&n);
    for(i=1;i<=n;i++)scanf("%lld%lld",&x[i],&y[i]);
    for(i=1;i<=n;i++)
        for(j=i+1;j<=n;j++)
            map[j][i]=map[i][j]=getDist(i,j);
    for(i=1;i<=n;i++)
        for(j=i+1;j<=n;j++){
            left=min(left,map[i][j]);
            right=max(right,map[i][j]);
        }
    while(left+1<right){
        mid=(left+right)/2;
        if(judge(mid))right=mid;
        else left=mid;
    }
    printf("%lld\n",right);
    return 0;
}


#10016 「一本通 1.2 练习 3」灯泡

//Light Bulb ZOJ - 3203

//#10016. 「一本通 1.2 练习 3」灯泡
//在线测评地址https://loj.ac/problem/10016
//在线测评地址https://vjudge.net/problem/ZOJ-3203
//人距灯泡的水平距离为x,推导L与x的关系
//从公式上,可以看出,L是关于x的类开口向下的抛物线(凸形更精准)
//找最大值,采用三分。

//算出影长公式,编好程序,设人距灯的水平距离为x,L=D-x+(x*H-D*(H-h))/x,测试
//发现样例中2 0.5 3这组数据对不上
//参看https://blog.csdn.net/scf0920/article/details/46050863代码
//发现是左边界没选好,不能随意的选个0
//参看https://blog.csdn.net/Baiyi_destroyer/article/details/79718020才明白左边界的得出,摘抄如下 
//设人距灯的距离为x,从灯正下方走到影子正好到墙角,影子的长度不断增加,此时由相似三角形得影子长度为D-h*D/H;
//left=D-D*h/H,right=D;//此处写成 left=0,造成 2 0.5 3 输出一直对不上
//该题最大的难点在于,影长要分段讨论
//影子只在地上时,随着x是逐渐增大;在地上与墙上时,是一个凸形函数。
//样例终于通过,提交AC。2018-10-10 19:45 
#include <stdio.h>
#define eps 1e-12
double H,h,D;
double L(double x){
    return D-x+(x*H-D*(H-h))/x;
}
int main(){
    int T;
    double left,right,mid,mmid;
    scanf("%d",&T);
    while(T--){
        scanf("%lf%lf%lf",&H,&h,&D);
        left=D-D*h/H,right=D;//此处写成 left=0,造成 2 0.5 3 输出一直对不上 
        while(left+eps<right){
            mid=(left+right)/2;
            mmid=(mid+right)/2;
            if(L(mid)>L(mmid))right=mmid;
            else left=mid;
        }
        printf("%.3lf\n",L(left));
    }
    return 0;
}

//Light Bulb ZOJ - 3203

//受https://blog.csdn.net/jingqi814/article/details/11993565启发 

//方法二,求导,
//但发现2 1 0.5;2 0.5 3两组数据均未对上。
//仔细想了想,还是边界的问题。 
//跟踪了样例数据,证实了猜想。

//样例通过,提交AC。2018-10-10 21:00 
#include <stdio.h>
#include <math.h>
int main(){
    int T;
    double H,h,D,x,L,left,right;
    scanf("%d",&T);
    while(T--){
        scanf("%lf%lf%lf",&H,&h,&D);
        x=sqrt(D*(H-h));//对 L=D-x+H-D*(H-h)/x求导后,计算出的x的值。 
        left=D-D*h/H;
        right=D;
        if(right<x)x=right;//x在right的右侧 
        else if(x<left)x=left;//x在left的左侧。 
        L=D-x+H-D*(H-h)/x;
        printf("%.3lf\n",L);
    }
    return 0;
}
 

#10017 「一本通 1.2 练习 4」传送带

//#10017. 「一本通 1.2 练习 4」传送带
//在线测评地址https://loj.ac/problem/10017
//P2571 [SCOI2010]传送带
//在线测评地址https://www.luogu.org/problemnew/show/P2571
//仔细读题,发现没什么感觉。
//AB与CD 位置关系,可能平行,内八字,外八字,交叉。
//此文思路讲得还可以https://blog.csdn.net/sadnohappy/article/details/52206450摘抄如下:
//这题我们先假设在AB线段确定了一个点(x,y),那么(x,y)到CD线段的一个点(x′,y′)的距离加上(x′,y′)
//到D点的时间显然是呈一个二次函数。于是我们可以在AB线段上枚举一个点,三分CD上的点,然后取最优即可。
//但是,这样可能会超时,我们考虑点A到(x,y)
//的时间随着距离增大而增大,这说明这里的函数是一条直线,那么显然(x,y)也可以三分出来。这样时间复杂度是O(log23L)(L是线段最长距离),所以是很快的。
//此文代码写得不错http://hzwer.com/4255.html
//三分套三分
//一开始,除错,r,q ,样例都无法通过。
//若不加fabs 洛谷中,测试点7,8WA。
//修改,提交AC。2018-10-15 19:30
#include <stdio.h>
#include <math.h>
#define eps 1e-6
double ax,ay,bx,by,cx,cy,dx,dy,p,q,r;
double dis(double x1,double y1,double x2,double y2){
    return sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
}
double judge(double x,double y){
    double lx,ly,rx,ry,xmid,xmmid,ymid,ymmid,t1,t2;
    lx=cx,rx=dx,ly=cy,ry=dy;
    while(fabs(rx-lx)>eps||fabs(ry-ly)>eps){//此处写成 while(lx+eps<rx||ly+eps<ry)
        xmid=(lx+rx)/2,xmmid=(xmid+rx)/2;
        ymid=(ly+ry)/2,ymmid=(ymid+ry)/2;
        t1=dis(ax,ay,x,y)/p+dis(x,y,xmid,ymid)/r+dis(xmid,ymid,dx,dy)/q;//此处写成 t1=dis(ax,ay,x,y)/p+dis(x,y,xmid,ymid)/q+dis(xmid,ymid,dx,dy)/r;
        t2=dis(ax,ay,x,y)/p+dis(x,y,xmmid,ymmid)/r+dis(xmmid,ymmid,dx,dy)/q;//此处写成 t2=dis(ax,ay,x,y)/p+dis(x,y,xmmid,ymmid)/q+dis(xmmid,ymmid,dx,dy)/r;
        if(t1<t2)rx=xmmid,ry=ymmid;
        else lx=xmid,ly=ymid;
    }
    return dis(ax,ay,x,y)/p+dis(x,y,lx,ly)/r+dis(lx,ly,dx,dy)/q;//此处写成 dis(ax,ay,x,y)/p+dis(x,y,lx,ly)/q+dis(lx,ly,dx,dy)/r;
}
int main(){
    double lx,ly,rx,ry,xmid,xmmid,ymid,ymmid;
    scanf("%lf%lf%lf%lf%lf%lf%lf%lf",&ax,&ay,&bx,&by,&cx,&cy,&dx,&dy);
    scanf("%lf%lf%lf",&p,&q,&r);
    lx=ax,rx=bx,ly=ay,ry=by;
    while(fabs(rx-lx)>eps||fabs(ry-ly)>eps){//此处写成 while(lx+eps<rx||ly+eps<ry)
        xmid=(lx+rx)/2,xmmid=(xmid+rx)/2;
        ymid=(ly+ry)/2,ymmid=(ymid+ry)/2;
        if(judge(xmid,ymid)<judge(xmmid,ymmid))
            rx=xmmid,ry=ymmid;
        else
            lx=xmid,ly=ymid;
    }
    printf("%.2lf\n",judge(lx,ly));
    return 0;
}

2018-10-15 19:36 AC该节内容。

猜你喜欢

转载自blog.csdn.net/mrcrack/article/details/82941977