首页 > 其他 > 详细

codeforces 606 E. Freelancer's Dreams(凸包)

时间:2020-05-08 09:09:15      阅读:51      评论:0      收藏:0      [点我收藏+]

传送门:http://codeforces.com/contest/606/problem/E

解题思路:

  可以证明最终选取的项目不会超过2个。可以在坐标轴上画图证明。将所有的$(a_{i},b_a{i})$视为二维坐标轴上的点$(x,y)$,对于任意两点$(x_{1},y_{1}),(x_{2},y_{2})$,将这两点连起来,所形成的的线段上任意一点为一天的可以获得的量。而最优解,必然是取其与点$(p,q)$的交点(若线段有交点),因为其他的点花费更大。可以先做个预处理,保证每个点只有左上和右下有点,其余的点删除(即不存在$x_{i}<y{i},x{j}<y_{j}$的情况,因为j肯定比x更优,i点不会用到)。

  下面证明不存在三个项目:假设选取了三个项目A,B,C,如图所示,必然是连接了线段BC,然后在线段BC上取一点D,连接AD,最后取得是线段AD与点$(0,0)$和点$(p,q)$所形成的的直线的交点。可以看成,如果AC一定比AD跟优,因为B点一定在他下方,因此不存在三点以上情况,只有两点构成。在仔细想一下,一定是凸包上相邻两个点与该点的交点。所以,我们可以求一个凸包,然后计算凸包相邻两点所形成的线段(注意,是线段,不是直线,这里wa了一发)的交点,形成的交点就是单天的获取方案。直接p除以其横坐标即可。当然这是取两个项目的情况,取一个项目的暴力判断一下即可,取min即可。

技术分享图片

  比赛的时候发现自己凸包的板子写的天花乱坠,既没有类封装,也没有函数封装,打了半天没打出来。

  最后贴上AC代码。

#include<bits/stdc++.h>
 
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
typedef pair <double,double> pii;
#define rep(i,x,y) for(int i=x;i<y;i++)
#define rept(i,x,y) for(int i=x;i<=y;i++)
#define per(i,x,y) for(int i=x;i>=y;i--)
#define all(x) x.begin(),x.end()
#define pb push_back
#define fi first
#define se second
#define mes(a,b) memset(a,b,sizeof a)
#define mp make_pair
#define dd(x) cout<<#x<<"="<<x<<" "
#define de(x) cout<<#x<<"="<<x<<"\n"
#define debug() cout<<"I love Miyamizu Mitsuha forever.\n"
const int inf=0x3f3f3f3f;
const int maxn=1e5+5;
pii p[maxn];
 
bool comp1(const pii &s1,const pii &s2)
{
    return s1.se>s2.se||(s1.se==s2.se&&s1.fi>s2.fi);
}
 
pii point[maxn];
double dis(const pii &s1,const pii &s2)
{
    return sqrt((s1.fi-s2.fi)*(s1.fi-s2.fi)+(s1.se-s2.se)*(s1.se-s2.se));
}
pii operator -(const pii &s1,const pii &s2)
{
    return mp(s1.fi-s2.fi,s1.se-s2.se);
}
double chaji(const pii &s1,const pii &s2)
{
    return s1.fi*s2.se-s1.se*s2.fi;
}
bool comp(const pii &s1,const pii &s2)
{
    double x=chaji(s1-point[0],s2-point[0]);
    if( x>0|| (x==0&&fabs(s1.fi-point[0].fi)<fabs(s2.fi-point[0].fi)) ) return 1;
    else return 0;
}
 
int graham(pii point[],int n)//计算凸包
{
    int p=0,cnt=0;
    rep(i,1,n)
        if( point[i].se<point[p].se||(point[i].se==point[p].se&&point[i].fi<point[p].fi) )
            p=i;
    swap(point[0],point[p]);
    sort(point+1,point+n,comp);
    cnt=2;
    rep(i,2,n)
    {
        while(cnt>=2&&chaji( point[cnt-1]-point[i],point[cnt-2]-point[i] )>=0 ) cnt--;
        point[cnt++]=point[i];
    }
    return cnt;
}
 
double a,b;
pii node(pii s1,pii s2)
{
    double x=( (s1.se*s2.fi-s1.fi*s2.se)/(s2.fi-s1.fi) )/(b/a-(s2.se-s1.se)/(s2.fi-s1.fi));
    double y=b/a*x;
    return mp(x,y);
}
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    int n;
    cin>>n>>a>>b;
    rep(i,0,n) cin>>p[i].fi>>p[i].se;
    sort(p,p+n,comp1);
    int cnt=0;
    p[cnt++]=p[0];
    double mal=p[0].fi;
    rep(i,1,n)
    {
        if(p[i].fi<=mal) continue;
        mal=p[i].fi;
        p[cnt++]=p[i];
    }
    n=cnt;
    if(n==1)
    {
        cout<<fixed<<setprecision(10)<<max(a/p[0].fi,b/p[0].se);//仅有一点时,无法计算凸包,特判
        return 0;
    }
    cnt=graham(p,n);
    double ans=1e18;
    rep(i,0,cnt-1)
    {
        pii point=node(p[i],p[i+1]);//计算交点
        if(point.fi<=p[i].fi&&point.fi>=p[i+1].fi) ans=min(ans,a/point.fi);//交点在线段上才可以选取
    }
    rep(i,0,cnt) ans=min(ans,max(a/p[i].fi,b/p[i].se));//取单点的情况
    cout<<fixed<<setprecision(10)<<ans<<"\n";
    return 0;
}

 

codeforces 606 E. Freelancer's Dreams(凸包)

原文:https://www.cnblogs.com/FZUzyz/p/12847029.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!