zoukankan      html  css  js  c++  java
  • 【NOI2007T2】货币兑换-DP斜率优化+CDQ分治

    测试地址:货币兑换
    做法:大名鼎鼎的论文题,难度确实不容小觑……足足做了一天仍然不能AC,但是只有一个点RE,应该是一些玄学瑕疵,但是思路整体是对的,所以……将就着看吧……
    本题需要用到DP斜率优化和CDQ分治。
    首先根据提示,每一天操作只有三种:买入、卖出、卖出再买入,鉴于卖出的一定是前面某一天留下的金券,那么设f(i)为第i天结束后手中最多的人民币,x(i)y(i)为第i天结束后当f(i)最大时所能得到的A券和B券的数量,那么状态转移方程如下:
    f(i)=max(f(i1),x(j)×ai+y(j)×bi)(1j<i)
    y(i)=f(i)ratei×ai+bi
    x(i)=y(i)×ratei
    知道了第一个式子之后,下面两个式子推推就出来了。注意到这是一个O(N^2)的式子,而N达到100000,必须想办法优化。
    我们发现式子x(j)×ai+y(j)×bi可以改写为[x(j)×aibi+y(j)]×bi,注意到bi是一个由i唯一确定的常量,所以当中括号内的式子最大时,这个式子就越大,可以不管它。我们设k(i)aibi,括号内的式子等于G,那么G=k(i)x(j)+y(j),则y(j)=k(i)x(j)+G,那么要使G最大,就是要使一条斜率为k(i)的直线穿过任一个点(x(j),y(j)),使得截距最大,所以可以进行斜率优化,维护一个上凸壳即可。
    然而没有那么简单,注意到我们之前做过的题目中,状态点的横坐标是单调的,斜率也是单调的,我们就可以用单调队列维护凸壳,然而这一题中横坐标既不单调,斜率也不单调,那要怎么维护凸壳呢?这里我们当然可以用平衡树来维护凸壳,使得总复杂度达到O(NlogN),然而这样写的话编程复杂度简直爆炸。
    我们可以借助CDQ分治的思想来解决这一题,到了这一步的话,网上的题解很多,我不保证我能写的比他们好,所以还是请读者们自行去查找吧。我的代码是O(Nlog2N)的,然而有方法可以优化到和平衡树同阶的O(NlogN),本人学艺不精,还需要学习一个……
    以下是本人代码(90分RE,又臭又长,强烈不推荐阅读):

    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define inf 1e9
    using namespace std;
    int n,p0[100010],forsort[100010];
    int up[100010],t;
    double s,a[100010],b[100010],rate[100010],f[100010];
    struct point
    {
      double x,y;
      int id;
      point operator - (point a) const
      {
        point s;
        s.x=x-a.x;
        s.y=y-a.y;
        return s;
      }
    }p[100010];
    
    double multi(point a,point b)
    {
      return a.x*b.y-b.x*a.y;
    }
    
    bool cmp(point x,point y)
    {
      int i=x.id,j=y.id;
      point s1,s2;
      s1.x=b[i],s1.y=-a[i];
      s2.x=b[j],s2.y=-a[j];
      return multi(s1,s2)<=0;
    }
    
    bool cmpid(point a,point b)
    {
      return a.id<b.id;
    }
    
    void solve(int l,int r)
    {
      int mid=(l+r)>>1;
      if (l==r) return;
      solve(l,mid);
    
      t=0;
      for(int i=l;i<=mid;i++)
      {
        while(t>1&&multi(p[up[t]]-p[up[t-1]],p[p0[i]]-p[up[t]])>=0) t--;
        up[++t]=p0[i];
      }
    
      if (mid==781)
        mid++,mid--;
      int lp,rp;
      lp=1;
      sort(p+mid+1,p+r+1,cmp);
      for(int i=mid+1;i<=r;i++)
      {
        point s;
        s.x=b[p[i].id],s.y=-a[p[i].id];
        while(lp<t&&multi(p[up[lp+1]]-p[up[lp]],s)<=0)
          lp++;
        int v=p[i].id,j=up[lp];
        if (f[v]<max(f[v-1],p[j].x*a[v]+p[j].y*b[v]))
        {
          f[v]=max(f[v-1],p[j].x*a[v]+p[j].y*b[v]);
          p[i].y=f[v]/(rate[v]*a[v]+b[v]);
          p[i].x=rate[v]*p[i].y;
        }
      }
      sort(p+mid+1,p+r+1,cmpid);
    
      solve(mid+1,r);
    
      lp=l,rp=mid+1;
      for(int i=l;i<=r;i++)
      {
        if (rp>r||(lp<=mid&&p[p0[lp]].x<p[p0[rp]].x)) forsort[i]=p0[lp],lp++;
        else forsort[i]=p0[rp],rp++;
      }
      for(int i=l;i<=r;i++) p0[i]=forsort[i];
    }
    
    int main()
    {
      scanf("%d%lf",&n,&s);
      for(int i=1;i<=n;i++)
      {
        scanf("%lf%lf%lf",&a[i],&b[i],&rate[i]);
        p0[i]=i;p[i].id=i;
      }
      p0[0]=0;p[0].id=0;
      f[0]=s,a[0]=0,b[0]=0,rate[0]=1;
      p[0].x=p[0].y=0;
      for(int i=0;i<=100009;i++) p[i].id=i;
    
      solve(0,n);
    
      printf("%.3lf
    ",f[n]);
      return 0;
    }
    
  • 相关阅读:
    POJ3159 Candies —— 差分约束 spfa
    POJ1511 Invitation Cards —— 最短路spfa
    POJ1860 Currency Exchange —— spfa求正环
    POJ3259 Wormholes —— spfa求负环
    POJ3660 Cow Contest —— Floyd 传递闭包
    POJ3268 Silver Cow Party —— 最短路
    POJ1797 Heavy Transportation —— 最短路变形
    POJ2253 Frogger —— 最短路变形
    POJ1759 Garland —— 二分
    POJ3685 Matrix —— 二分
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793696.html
Copyright © 2011-2022 走看看