货币兑换Cash - bzoj 1492 cdq分治+斜率优化

货币兑换Cash

Time Limit: 5 Sec   Memory Limit: 64 MB
Submit: 2179   Solved: 974
[ Submit][ Status][ Discuss]

Description

Input

第一行两个正整数N、S,分别表示小Y 能预知的天数以及初始时拥有的钱数。 接下来N 行,第K 行三个实数AK、BK、RateK,意义如题目中所述

Output

只有一个实数MaxProfit,表示第N 天的操作结束时能够获得的最大的金钱 数目。答案保留3 位小数。

Sample Input

3 100
1 1 1
1 2 2
2 2 3

Sample Output

225.000

HINT



测试数据设计使得精度误差不会超过10-7。
对于40%的测试数据,满足N ≤ 10;
对于60%的测试数据,满足N ≤ 1 000;
对于100%的测试数据,满足N ≤ 100 000;


思路:令F[i]表示第i天获得的最大B卷数量。
枚举上一次交易日j
F[i]=max{ans,Rate[j]*F[j]*A[i]+F[j]*B[i]}/(rate[i]*a[i]+b[i])
O(n^2)
观察括号内的表达式,可以发现决策J优于决策K的条件:(rate[j]*f[j]-rate[k]*f[k])/(f[j]-f[k])>-b[i]/a[i]
上面这个式子的左边,一般记成slope(j,k)

Slope(j,k) > -b[i]/a[i]
令G[i] = rate[i]*f[i],在二维平面上定义点Xi=(Fi,Gi)
Slope(j,k)就是通过Xj和Xk的斜率

暴力代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node
{
    double a,b,rate,k;
    int id;
}p[100010];
double f[100010],ans[100010],S;
int n,m;
int main()
{
    int i,j,k;
    while(~scanf("%d%lf",&n,&S))
    {
        for(i=1;i<=n;i++)
        {
            scanf("%lf%lf%lf",&p[i].a,&p[i].b,&p[i].rate);
            p[i].k=-p[i].b/p[i].a;
            p[i].id=i;
        }
        ans[0]=S;
        for(i=1;i<=n;i++)
        {
            ans[i]=ans[i-1];
            for(j=1;j<i;j++)
               ans[i]=max(ans[i],f[j]*p[j].rate*p[i].a+f[j]*p[i].b);
            f[i]=ans[i]/(p[i].rate*p[i].a+p[i].b);
        }
        printf("%.3f\n",ans[n]);
    }
}

AC代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node
{
    double a,b,rate,k;
    int id;
}p[100010],tmp[100010];
struct node2
{
    double x,y;
}A[100010],C[100010];
double eps=1e-9;
int dcmp(double x)
{
    return (x>eps)-(x<-eps);
}
bool cmp(node a,node b)
{
    return a.k>b.k;
}
bool cmp2(node2 a,node2 b)
{
    return dcmp(a.x-b.x)<0 ||(dcmp(a.x-b.x)==0 && dcmp(a.y-b.y)<0);
}
double f[100010],ans[100010],S;
int n,m;
bool slopecomp(node2 a,node2 b,node2 c)
{
    double xa,xb,ya,yb,temp;
    xa=b.x-a.x;ya=b.y-a.y;
    xb=c.x-a.x;yb=c.y-a.y;
    temp=xa*yb-xb*ya;
    return dcmp(temp)<0;
}
void cdq(int l,int r)
{
    if(l==r)
    {
        ans[l]=max(ans[l],ans[l-1]);
        f[l]=max(f[l],ans[l]/(p[l].rate*p[l].a+p[l].b));
        return;
    }
    if(l+1<=r)
    {
        int i,j,k,na=0,nb=0,nc=0,mi=(l+r)/2;
        int l1=l,l2=mi+1;
        for(i=l;i<=r;i++)
           if(p[i].id<=mi)
             tmp[l1++]=p[i];
           else
             tmp[l2++]=p[i];
        for(i=l;i<=r;i++)
           p[i]=tmp[i];
        cdq(l,mi);
        for(i=l;i<=mi;i++)
        {
            A[++na].x=f[i];
            A[na].y=f[i]*p[i].rate;
        }
        sort(A+1,A+1+na,cmp2);
        for(i=1;i<=na;i++)
        {
            while(nc>1 && !slopecomp(C[nc-1],C[nc],A[i]))
              nc--;
            C[++nc]=A[i];
        }
        j=0;
        for(i=mi+1;i<=r;i++)
        {
            while(j<nc)
            {
                if(C[j].x*p[i].b+C[j].y*p[i].a>=C[j+1].x*p[i].b+C[j+1].y*p[i].a)
                  break;
                j++;
            }
            ans[p[i].id]=max(ans[p[i].id],C[j].x*p[i].b+C[j].y*p[i].a);
            f[p[i].id]=ans[p[i].id]/(p[i].rate*p[i].a+p[i].b);
        }
        cdq(mi+1,r);
    }
}
int main()
{
    int i,j,k;
    while(~scanf("%d%lf",&n,&S))
    {
        memset(ans,0,sizeof(ans));
        memset(f,0,sizeof(f));
        ans[0]=S;
        for(i=1;i<=n;i++)
        {
            scanf("%lf%lf%lf",&p[i].a,&p[i].b,&p[i].rate);
            p[i].k=-p[i].b/p[i].a;
            p[i].id=i;
        }
        sort(p+1,p+1+n,cmp);
        cdq(1,n);
        printf("%.3f\n",ans[n]);
    }
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值