• bzoj 1492: [NOI2007]货币兑换Cash【贪心+斜率优化dp+cdq】


    参考:http://www.cnblogs.com/lidaxin/p/5240220.html
    虽然splay会方便很多,但是懒得写,于是写了cdq
    首先要想到贪心的思路,因为如果在某天买入是能得到最大收益的,那么应该用所有钱去买,相对的如果在某天卖出能得到最大收益,那么应该全部卖出
    方便起见,设( x[j]=f[j]/(a[j]*rate[j]+b[j])*rate[j] )表示第j天最多可以拥有的A货币的数量,y[j]=f[j]/(a[j]*rate[j]+b[j])表示第j天最多可以拥有的B货币的数量
    于是dp方程就可以写作( f[i]=max{f[i-1],x[j]*a[i]+y[j]*b[i]} )
    然后按着斜率优化套路推一推就变成了( y[j]=-a[i]/b[i]*x[j]+f[i]/b[i] ),按照y=kx+b的形式就是y=y[j],k=-a[i]/b[i],x=x[j],b=f[i]/b[i]
    然后问题在于这个斜率不是单调的,所以考虑如何将点插入上凸壳
    splay显然是可以的,但是写起来太麻烦了
    仔细想想,dp的过程就相当于n次询问+插入
    这个似乎可以用cdq分治,设f[i]的id为i
    首先按照斜率k排序,对于当前要处理的区间(l,r),先把id<=mid的丢到(l,mid)中,把剩下的丢进(mid+1,r),递归处理出(l,mid),然后用处理出的点来构成一个上凸壳,用这个上凸壳来更新(mid+1,r)中的f,因为是按斜率排序的,所以用双指针即可
    然后递归处理(mid+1,r),最后把点集按极角排序即可

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    const int N=100005,inf=1e9;
    int n,s[N];
    double f[N];
    struct dian
    {
    	double x,y;
    	bool operator < (const dian &b) const
    	{
    		return (x<=b.x)||(x==b.x&&y<=b.y);
    	}
    }p[N],q[N];
    struct qwe
    {
    	double q,a,b,r,k;
    	int id;
    }a[N],b[N];
    double slv(int i,int j)
    {
    	if(i==0)
    		return -inf;
    	if(j==0)
    		return inf;
    	if(p[i].x==p[j].x)
    		return -inf;
    	return (p[i].y-p[j].y)/(p[i].x-p[j].x);
    }
    bool cmp(const qwe &a,const qwe &b)
    {
    	return a.k<b.k;
    }
    void wk(int l,int r)
    {
    	if(l==r)
    	{
    		f[l]=max(f[l],f[l-1]);
            p[l].y=f[l]/(a[l].a*a[l].r+a[l].b);
            p[l].x=p[l].y*a[l].r;
    		return;
    	}
    	int mid=(l+r)>>1,top=0,l1=l,l2=mid+1;
        for(int i=l;i<=r;i++)
    	{
            if(a[i].id<=mid) 
    			b[l1++]=a[i];
            else 
    			b[l2++]=a[i];
    	}
        for(int i=l;i<=r;i++) 
    		a[i]=b[i];
    	wk(l,mid);
    	for(int i=l;i<=mid;i++)
    	{
    		while(top>=2&&slv(i,s[top])>=slv(s[top],s[top-1]))
    			top--;
    		s[++top]=i;
    	}
    	for(int i=r,j=1;i>=mid+1;i--)
    	{
    		while(j<top&&a[i].k<=slv(s[j],s[j+1]))
    			j++;
    		f[a[i].id]=max(f[a[i].id],p[s[j]].x*a[i].a+p[s[j]].y*a[i].b);
    	}
    	wk(mid+1,r);
    	l1=l,l2=mid+1;
    	for(int i=l;i<=r;i++)
    	{
    		if((p[l1]<p[l2]||l2>r)&&l1<=mid)
    			q[i]=p[l1++];
    		else
    			q[i]=p[l2++];
    	}
    	for(int i=l;i<=r;i++)
    		p[i]=q[i];
    }
    int main()
    {
    	scanf("%d%lf",&n,&f[0]);
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%lf%lf%lf",&a[i].a,&a[i].b,&a[i].r);
    		a[i].k=-a[i].a/a[i].b,a[i].id=i;
    	}
    	sort(a+1,a+1+n,cmp);
    	wk(1,n);
    	printf("%.3f
    ",f[n]);
    	return 0;
    }
    
  • 相关阅读:
    Go语言指针
    程序员 需要掌握得600个英语单词
    Go语言管道
    Go语言容器
    Go语言切片
    Go语言类型转换
    Go语言数组
    LR静态存储/动态存储/指针变量脚本说明
    MQ报错2009/2085解决方法
    Windows性能监控监视器(perfmon使用)
  • 原文地址:https://www.cnblogs.com/lokiii/p/9199587.html
Copyright © 2020-2023  润新知