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;
    }
    
  • 相关阅读:
    PYTHON压平嵌套列表
    linux下IPTABLES配置详解
    Python面试必须要看的15个问题
    两个实用的Python的装饰器
    Python的16个“坑”
    python实现不可修改的常量
    51nod-1322: 关于树的函数
    51nod-1310: Chandrima and XOR
    51nod-1296: 有限制的排列
    51nod-1277: 字符串中的最大值
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793696.html
Copyright © 2011-2022 走看看