bzoj2765 铁人双项比赛

Description

铁人双项比赛是吉林教育学院的一项传统体育项目。该项目比赛由长跑和骑自行车组成,参赛选手必须先完成k公里的长跑,然后完成r公里的骑车,才能到达终点。每个参赛选手所擅长的项目不同,有的擅长长跑,有的擅长骑车。如果总赛程s=k+r一定,那么K越大,对擅长长跑的选手越有利;k越小,对擅长骑车的选手越有利。
 
现在给定总赛程s,以及每个选手长跑和骑车的平均速度,请你求出对于某个指定的选手最有利的k和r。所谓最有利,是指选择了这个k和r后,该选手可以获得冠军,且领先第2名尽量地多。

Input

你的程序从文件读入输入数据。
输入的第一行是两个正整s和n,s表示总赛程(单位为公里,s≤231),n表示参赛总人数(2≤n≤100)。
接下来的n行每行是两个实数,分别表示每个选手长跑的平均速度和骑车的平均速度(单位为千米/小时)。
第n个选手就是指定的选手,你的任务是求出对他最有利的k和r。

Output

你的程序的输出包括三个数k,r, t,分别表示对第n号选手最有利的k和r(浮点数,保留小数点后2位),以及在选择k和r的情况下,第n号选手最多可以领先第2名多少秒(四舍五入到整数);如果另一个选手和该选手并列第一,则t i=0。倘若无论选择什么k,r都不能使第n号选手获胜,则输出“NO”。

s确定后,选手完成比赛的时间是关于k的一次函数,图像为直线

问题为求k使f(k)=(选手1~n-1的最高成绩-选手n成绩)取得最大值

f(k)的图像为上凸的折线,最大值在折点或两端取得

枚举每个可能为折点的直线交点求f(k)的最大值

若数据范围更大可以求半平面交直接处理出折点

#include<cstdio>
int n;
double s;
double v1,v2;
double a[105],b[105];
double km=0,ans=-1.0e50;
void cal(double x){
    if(x<0||x>s)return;
    double y=1.0e50,c;
    for(int i=1;i<n;i++){
        c=a[i]*x+b[i];
        if(c<y)y=c;
    }
    y-=a[n]*x+b[n];
    if(y>ans)ans=y,km=x;
}
int main(){
    scanf("%lf%d",&s,&n);
    for(int i=1;i<=n;i++){
        scanf("%lf%lf",&v1,&v2);
        a[i]=1.0/v1-1.0/v2;
        b[i]=s/v2;
    }
    for(int i=1;i<n;i++){
        for(int j=i+1;j<n;j++){
            cal((b[j]-b[i])/(a[i]-a[j]));
        }
    }
    cal(0);
    cal(s);
    if(ans>=0)printf("%.2lf %.2lf %.0lf",km,s-km,ans*3600.0);
    else puts("NO");
    return 0;
}
原文地址:https://www.cnblogs.com/ccz181078/p/5173395.html