BZOJ1492 货币兑换 CDQ分治优化DP
1492: [NOI2007]货币兑换Cash
Time Limit: 5 Sec Memory Limit: 64 MB
Description
.png)
.png)
Input
对于40%的测试数据,满足N ≤10;
对于60%的测试数据,满足N ≤1 000;
对于100%的测试数据,满足N ≤100 000;
Output
只有一个实数MaxProfit,表示第N天的操作结束时能够获得的最大的金钱数目。答案保留3位小数。
Sample Input
1 1 1
1 2 2
2 2 3
Sample Output
HINT
.png)
(转载请注明原文地址:http://www.cnblogs.com/LadyLex/p/8028556.html )
终于把CDQ维护凸壳学了……
那么既然题目已经给提示2了,我们就可以针对性的设$f[i]$为第i天获得的最多A券数,$ans[i]$为第i天的最大获利,那么我们要求的就是ans[n]了。
这个dp显然是可以$O(n^{2})$解决的……但是这样拿不了后面的分数。
然后你就想啊,肯定要优化啊……然后你就可以化出一个斜率的式子来。
对于$ans[i]$的决策,我们设两天j和k,j比k优秀的话,就会有
$(f[j]-f[k])*a[i]+(f[j]/rate[j]-f[k]/rate[k])*b[i]>0$
再设$g[i]=f[i]/rate[i]$(也就是第i天获得的最多B券数),为了处理不等式的符号我们设$f[j]<f[k]$
所以有:
$(g[j]-g[k])*b[i]>-(f[j]-f[k])*a[i]$
$(g[j]-g[k])/(f[j]-f[k])<-a[i]/b[i]$
(当然,实际情况是有$f[j]==f[k]$,即斜率不存在的情况存在的,到时候还要讨论。)
那么我们转化到一些坐标为(f[i],g[i])的二维平面的点上来。
我们建立一个这些点的上凸壳,然后在凸壳上二分最靠左的最后一个满足$k(point(x),point(x+1))<-a[i]/b[i]$的点x,
那么$x+1$就是最优秀的取值,也即本次决策点。
但是你发现这个f[i]不随i单调……那么我们考虑splay或者CDQ
打个J的splay啊
那么我们CDQ维护凸壳并且决策就好了……
具体实现是让$f[i]$有序之后按照正常方法建凸壳,然后让$-a[i]/b[i]$有序(我是从大到小排序),单调扫一边完成决策。
怎么让这俩有序呢……一是大力sort,复杂度$O(nlog^{2}n)$,一是归并排序,复杂度$O(nlogn)$
两种方法我都打了一下……感觉少个$logn$没快到哪去,也没长到哪去……
如果复杂度可行还是打$log^{2}$吧……
两份代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define eps 1e-8
#define N 100010
#define db double
#define inf 0x7fffffff
#define sign(a) (((a)>-eps)-((a)<eps))
int n,top,sta[N],id[N],tmp[N],idk[N],tmpk[N],match[N];
db ans[N],ak[N],bk[N],rate[N],f[N],g[N];
inline db max(db a,db b){return a>b?a:b;}
inline bool comp(const int &a,const int &b)
{return f[a]<f[b] || ( sign(f[a]-f[b])==&&g[a]<g[b] );}
inline double k(int a,int b)
{
if(sign(f[a]-f[b])==)return sign(g[a]-g[b])*inf;
return (g[a]-g[b])/(f[a]-f[b]);
}
inline bool compk(const int &a,const int &b)
{return sign( (-ak[a]/bk[a]) - (-ak[b]/bk[b]) ) > ;}
inline void CDQ(int l,int r)
{
if(l==r){g[l]=f[l]/rate[l];return;}
register int hd1,i,t,mi=l+r>>,p=l-,q=mi,h=l-;
for(i=l;i<=r;++i)
if(match[idk[i]]<=mi)tmpk[++p]=idk[i];
else tmpk[++q]=idk[i];
for(i=l;i<=r;++i)idk[i]=tmpk[i];
CDQ(l,mi);
for(top=,i=l;i<=mi;++i)
{
while(top> && k(sta[top-],sta[top]) < k(sta[top],id[i]) )--top;
sta[++top]=id[i];
}
for(hd1=,i=mi+;i<=r;++i)
{
t=match[idk[i]];
while( hd1<top&&sign( k(sta[hd1],sta[hd1+]) - (-ak[t]/bk[t]) ) >= )++hd1;
ans[t]=max(ans[t],f[sta[hd1]]*ak[t]+g[sta[hd1]]*bk[t]);
}
for(i=mi+;i<=r;++i)
t=match[idk[i]],ans[t]=max(ans[t],ans[t-]),f[t]=ans[t]*rate[t]/(ak[t]*rate[t]+bk[t]);
CDQ(mi+,r);
p=l,q=mi+,h=l;
while(p<=mi&&q<=r)
if(comp(id[p],id[q]))tmp[h++]=id[p++];
else tmp[h++]=id[q++];
while(p<=mi)tmp[h++]=id[p++];
while(q<=r)tmp[h++]=id[q++];
for(i=l;i<=r;++i)id[i]=tmp[i];
}
int main()
{
register int i,j;
scanf("%d%lf",&n,&ans[]);
for(i=;i<=n;++i)
scanf("%lf%lf%lf",&ak[i],&bk[i],&rate[i]);
f[]=ans[]*rate[]/(ak[]*rate[]+bk[]);
for(i=;i<=n;++i)id[i]=idk[i]=match[i]=i;
sort(match+,match+n+,compk);
CDQ(,n);
printf("%.3f\n",ans[n]);
}
nlogn
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define eps 1e-8
#define N 100010
#define db double
#define inf 0x7fffffff
#define sign(a) (((a)>-eps)-((a)<eps))
int n;
db ans[N],ak[N],bk[N],rate[N],f[N],g[N];
inline db max(db a,db b){return a>b?a:b;}
int top,sta[N];
int id[N],tmp[N],idk[N];
inline bool comp(const int &a,const int &b)
{
return f[a]<f[b] || ( sign(f[a]-f[b])==&&g[a]<g[b] );
}
inline double k(int a,int b)
{
if(sign(f[a]-f[b])==)
return sign(g[a]-g[b])*inf;
return (g[a]-g[b])/(f[a]-f[b]);
}
inline bool compk(const int &a,const int &b)
{
return sign( (-ak[a]/bk[a]) - (-ak[b]/bk[b]) ) > ;
}
inline void CDQ(int l,int r)
{
if(l==r){g[l]=f[l]/rate[l];return;}
register int hd1,i,mi=l+r>>;
CDQ(l,mi);
for(i=mi+;i<=r;++i)id[i]=i;sort(id+l,id+mi+,comp);
for(i=mi+;i<=r;++i)idk[i]=i;sort(idk+mi+,idk+r+,compk);
for(top=,i=l;i<=mi;++i)
{
while(top> && k(sta[top-],sta[top]) < k(sta[top],id[i]) )--top;//****
sta[++top]=id[i];
}
for(hd1=,i=mi+;i<=r;++i)
{
while( hd1<top&&sign( k(sta[hd1],sta[hd1+]) - (-ak[idk[i]]/bk[idk[i]]) ) >= )++hd1;
ans[idk[i]]=max(ans[idk[i]],f[sta[hd1]]*ak[idk[i]]+g[sta[hd1]]*bk[idk[i]]);
}
for(i=mi+;i<=r;++i)
ans[i]=max(ans[i],ans[i-]),f[i]=ans[i]*rate[i]/(ak[i]*rate[i]+bk[i]);
CDQ(mi+,r);
}
int main()
{
register int i,j;
scanf("%d%lf",&n,&ans[]);
for(i=;i<=n;++i)
scanf("%lf%lf%lf",&ak[i],&bk[i],&rate[i]);
f[]=ans[]*rate[]/(ak[]*rate[]+bk[]);
for(i=;i<=n;++i)id[i]=i;
CDQ(,n);
printf("%.3f\n",ans[n]);
}
nlog^2n
BZOJ1492 货币兑换 CDQ分治优化DP的更多相关文章
- 洛谷 P4093 [HEOI2016/TJOI2016]序列 CDQ分治优化DP
洛谷 P4093 [HEOI2016/TJOI2016]序列 CDQ分治优化DP 题目描述 佳媛姐姐过生日的时候,她的小伙伴从某宝上买了一个有趣的玩具送给他. 玩具上有一个数列,数列中某些项的值可能会 ...
- SPOJ LIS2 - Another Longest Increasing Subsequence Problem(CDQ分治优化DP)
题目链接 LIS2 经典的三维偏序问题. 考虑$cdq$分治. 不过这题的顺序应该是 $cdq(l, mid)$ $solve(l, r)$ $cdq(mid+1, r)$ 因为有个$DP$. #i ...
- luogu4093 序列 (cdq分治优化dp)
设f[i]是以i位置为结尾的最长满足条件子序列的长度 那么j能转移到i的条件是,$j<i , max[j]<=a[i] , a[j]<=min[i]$,其中max和min表示这个位置 ...
- P4027 [NOI2007]货币兑换(斜率优化dp+cdq分治)
P4027 [NOI2007]货币兑换 显然,如果某一天要买券,一定是把钱全部花掉.否则不是最优(攒着干啥) 我们设$f[j]$为第$j$天时用户手上最多有多少钱 设$w$为花完钱买到的$B$券数 $ ...
- [NOI2007]货币兑换 cdq分治,斜率优化
[NOI2007]货币兑换 LG传送门 妥妥的\(n \log n\)cdq做法. 这题用cdq分治也可以\(n \log n\)但是在洛谷上竟然比一些优秀的splay跑得慢真是见了鬼了看来还是人丑常 ...
- 【BZOJ1492】【Luogu P4027】 [NOI2007]货币兑换 CDQ分治,平衡树,动态凸包
斜率在转移顺序下不满足单调性的斜率优化\(DP\),用动态凸包来维护.送命题. 简化版题意:每次在凸包上插入一个点,以及求一条斜率为\(K\)的直线与当前凸包的交点.思路简单实现困难. \(P.s\) ...
- BZOJ3963: [WF2011]MachineWorks 【CDQ+斜率优化DP】*
BZOJ3963: [WF2011]MachineWorks Description 你是任意性复杂机器公司(Arbitrarily Complex Machines, ACM)的经理,公司使用更加先 ...
- 【BZOJ 1492】 [NOI2007]货币兑换Cash 斜率优化DP
先说一下斜率优化:这是一种经典的dp优化,是OI中利用数形结合的思想解决问题的典范,通常用于优化dp,有时候其他的一些决策优化也会用到,看待他的角度一般有两种,但均将决策看为二维坐标系上的点,并转化为 ...
- SRM12 T2夏令营(分治优化DP+主席树 (已更新NKlogN)/ 线段树优化DP)
先写出朴素的DP方程f[i][j]=f[k][j-1]+h[k+1][i] {k<i}(h表示[k+1,j]有几个不同的数) 显然时间空间复杂度都无法承受 仔细想想可以发现对于一个点 i ...
随机推荐
- mysql的查询使用explain的讲解
摘自:http://www.jb51.net/article/33736.htm 在 explain的帮助下,您就知道什么时候该给表添加索引,以使用索引来查找记录从而让select 运行更快.如果由于 ...
- 网络运营商名称显示&SIM名称显示
一 网络名称显示这部分比較复杂.Spec对这也有明白的规定,依据其优先级由高往低介绍(其优先级參考TS 22.101), 1. Enhanced Operator Name String. ...
- 汇编 push ,pop指令
知识点: PUSH POP CALL堆栈平衡 RETN指令 一.PUSH入栈指令 (压栈指令): 格式: PUSH 操作数 //sub esp,4 ;mov [esp],EBP 操作数 ...
- 【HNOI2016】网络
题面 题解 考虑整体二分. 定义整体二分函数solve(l, r, ql, qr)表示操作权值在\([l, r]\)中,对\([ql, qr]\)的询问进行二分. 这样的话check就会很简单,先按照 ...
- 【2017年9月10日更新】ABP配套代码生成器(ABP Code Generator)帮助文档,实现快速开发
ABP代码生成器介绍 ABP Code Generator 针对abp这个框架做了一个代码生成器,功能强大.分为两大功能点,一个是数据层,一个是视图层. 数据服务层:通过它,可以实现表设计.领域层初始 ...
- Gulp:插件编写入门
之前挖了个坑,准备写篇gulp插件编写入门的科普文,之后迟迟没有动笔,因为不知道该肿么讲清楚Stream这货,毕竟,gulp插件的实现不像grunt插件的实现那么直观. 好吧,于是决定单刀直入了.文中 ...
- 如何在内网安装compass
神器compass是肿么用这里不做介绍,因为我也不清楚,可参考官网:http://compass-style.org.这里主要介绍如何在内网安装compass. 首先介绍一般是如何安装compass的 ...
- cocos2dx渲染架构
2dx的时代UI树便利和渲染是没有分开的,遍历UI树的时候就渲染.3dx版本为了分离了ui树的遍历和渲染,先遍历生成渲染命令发到渲染队列,之后遍历渲染命令队列开始渲染.这样做的好处是渲染命令可以重用, ...
- sqli-labs 下载、安装
sqli-labs 下载.安装 下载地址:https://github.com/Audi-1/sqli-labs phpstudy:http://down.php.cn/PhpStudy2018021 ...
- C++ Makefile文件编写
对现有的一个C++动态库文件和调用程序,分别编写Makefile文件,从零开始,这里把自己弄明白的一些东西分享给大家. 1.必须明确Linux下,C++的编译器是g++,C语言的是gcc.网上大多数又 ...