P4027 [NOI2007]货币兑换

算法:dp+斜率优化

题面十分冗长,题意大概是有一种金券每天价值会有变化,你可以在某些时间点买入或卖出所有的金券,问最大收益

根据题意,很容易列出朴素的状态转移方程:

设\(f_i\)为第\(i\)天B券的数量,\(ans_j\)为以当前价格卖光第\(j\)天的金券可获得的收益,则

\(f_i=\max{ans_j}/(a_i*r_i+b_i)\)

\(O(n)\)求\(\max{ans_j}\),复杂度为\(O(n^2)\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
typedef long long LL;
const int MAXN=1024*100;
int N;
double S,a[MAXN],b[MAXN],r[MAXN],f[MAXN],ans;
int main(){
scanf("%d%lf",&N,&S);
for(int i=1;i<=N;i++){
scanf("%lf%lf%lf",&a[i],&b[i],&r[i]);
}
ans=S;
f[1]=ans*r[1]/(a[1]*r[1]+b[1]);
for(int i=1;i<=N;i++){
for(int j=1;j<i;j++){
ans=max(ans,f[j]*a[i]+f[j]/r[j]*b[i]);
}
f[i]=ans*r[i]/(a[i]*r[i]+b[i]);
}
printf("%.3lf",ans);
return 0;
}

然而此题要求\(O(nlogn)\)的做法,故朴素的dp无法AC,此时可以想到斜率优化

step1:转化方程

设\(a_i\)为第\(i\)天A券的价格,\(b_i\)为第\(i\)天B券的价格,\(ca_i\)为第\(i\)天A券的数量,\(cb_i\)为第\(i\)天B券的数量,\(f_i\)为第\(i\)天的最大收益

则\(f_i=\max{ca_j*a_i+cb_j*b_i}\)

∴如果j比k更优,有

\(ca_j*a_i+cb_j*b_i>ca_k*a_i+cb_k*b_i\)

∴\((cb_j-cb_k)*b_i>-a_i*(ca_j-ca_k)\)

∴\(\frac{cb_j-cb_k}{ca_j-ca_k}>-\frac{a_i}{b_i}\)

以\(ca\)为横坐标\(cb\)为纵坐标建立如图所示平面直角坐标系

剩下的就可以-斜率优化-

不过此题不比模板题,ca与cb不满足单调性,所以需要用平衡树或cdq等方法维护,此处用stl_set维护(因为是凸壳,所以斜率与横坐标同时满足单调性,可以用一个关键字查找)

此处切线只要找\(-\frac{a}{b}\)的lower_bound即可(代码中的query)

此处插入点要把当前位置两边的点都判断一下是否与上凸壳冲突,删除(代码中的insert)

实现就不难了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<set>
using namespace std;
typedef long long LL;
const int MAXN=1024*100;
int N;
double S,A[MAXN],B[MAXN],R[MAXN],F[MAXN],CA[MAXN],CB[MAXN];
inline double ABS(double x) { return x>0?x:-x; }
struct node {
double X,Y,K;
int flag;
node() { X=Y=K=flag=0; }
node(double x,double y) { X=x; Y=y; flag=0; }
friend inline bool operator<(node x,node y) {
if(x.flag||y.flag) { return x.K>y.K; }
return x.X<y.X;
}
friend inline bool operator==(node x,node y) { return ABS(x.X-y.X)<1e-8; }
friend inline double operator*(node x,node y) { return (y.Y-x.Y)/(y.X-x.X); }//斜率
inline bool error() { return X<-1e20||Y<-1e20; }
} error(-1e21,-1e21);
set<node> dq;
typedef set<node>::iterator ITER;
inline node next(node x) {
ITER ii=dq.upper_bound(x);
return ii==dq.end()?error:*ii;
}
inline node lower(node x) {
ITER ii=dq.lower_bound(x);
return ii==dq.end()?error:*ii;
}
inline node pre(node x) {
ITER ii=dq.lower_bound(x);
return ii==dq.begin()?error:*(--ii);
}
inline void insert(node x) {
if(dq.empty()) {
x.K=0;
dq.insert(x);
return;
}
node L=pre(x),R=lower(x);
if((L.error()&&x.Y<R.Y)||(!L.error()&&!R.error()&&L*x-L*R<1e-8)/**/||(x==R)) { return; }
R=next(x);
while(1) {
L=R;
R=next(L);
if(L.error()||R.error()||(x*L)-(L*R)>=1e-8) { break; }
dq.erase(L);
}
L=pre(x);
while(1) {
R=L;
L=pre(R);
if(L.error()||R.error()||(L*R)-(R*x)>=1e-8) { break; }
dq.erase(R);
}
L=pre(x);
R=next(x);
x.K=(L.error()?0:(L*x));
dq.insert(x);
if(!R.error()) {
dq.erase(R);
R.K=(x*R);
dq.insert(R);
}
}
inline double query(double x,double y) {
node ii;
ii.flag=1;
ii.K=-x/y;
ii=*(--dq.lower_bound(ii));
return ii.error()?0:ii.X*x+ii.Y*y;
}
int main() {
scanf("%d%lf",&N,&S);
for(int i=1; i<=N; i++) {
scanf("%lf%lf%lf",&A[i],&B[i],&R[i]);
}
F[1]=S;
CB[1]=S/(A[1]*R[1]+B[1]);
CA[1]=CB[1]*R[1];
insert(node(CA[1],CB[1]));
for(int i=2; i<=N; i++) {
F[i]=max(F[i-1],query(A[i],B[i]));
CB[i]=F[i]/(A[i]*R[i]+B[i]);
CA[i]=CB[i]*R[i];
insert(node(CA[i],CB[i]));
}
printf("%.3lf",F[N]);
return 0;
}

洛谷P4027 [NOI2007]货币兑换的更多相关文章

  1. 洛谷 P4027 [NOI2007]货币兑换 解题报告

    P4027 [NOI2007]货币兑换 题目描述 小 \(Y\) 最近在一家金券交易所工作.该金券交易所只发行交易两种金券:\(A\) 纪念券(以下简称 \(A\) 券)和 \(B\) 纪念券(以下简 ...

  2. LOJ 2353 & 洛谷 P4027 [NOI2007]货币兑换(CDQ 分治维护斜率优化)

    题目传送门 纪念一下第一道(?)自己 yy 出来的 NOI 题. 考虑 dp,\(dp[i]\) 表示到第 \(i\) 天最多有多少钱. 那么有 \(dp[i]=\max\{\max\limits_{ ...

  3. 洛谷P4027 [NOI2007]货币兑换(dp 斜率优化 cdq 二分)

    题意 题目链接 Sol 解题的关键是看到题目里的提示... 设\(f[i]\)表示到第\(i\)天所持有软妹币的最大数量,显然答案为\(max_{i = 1}^n f[i]\) 转移为\(f_i = ...

  4. P4027 [NOI2007]货币兑换(斜率优化dp+cdq分治)

    P4027 [NOI2007]货币兑换 显然,如果某一天要买券,一定是把钱全部花掉.否则不是最优(攒着干啥) 我们设$f[j]$为第$j$天时用户手上最多有多少钱 设$w$为花完钱买到的$B$券数 $ ...

  5. 洛谷 P2047 [NOI2007]社交网络 解题报告

    P2047 [NOI2007]社交网络 题目描述 在社交网络(\(social\) \(network\))的研究中,我们常常使用图论概念去解释一些社会现象.不妨看这样的一个问题.在一个社交圈子里有\ ...

  6. 洛谷——P2047 [NOI2007]社交网络

    P2047 [NOI2007]社交网络 $Floyd$,一眼看到就是他(博主是不小心瞄到了这个题的标签吧qwq) 这个题目只要预处理出$S$到$T$的最短路的条数即可,类似$Spfa$的更新方法 如果 ...

  7. 洛谷P2047 [NOI2007]社交网络 [图论,最短路计数]

    题目传送门 社交网络 题目描述 在社交网络(social network)的研究中,我们常常使用图论概念去解释一些社会现象.不妨看这样的一个问题.在一个社交圈子里有n个人,人与人之间有不同程度的关系. ...

  8. P4027 [NOI2007]货币兑换

    传送门 首先有一个显然的贪心,每次操作都要做到底,为了最优不会出现只卖一部分或者只买一部分的操作 所以设 $f[i]$ 表示前 $i$ 天得到的最大价值,那么对于每一个 $i$,枚举所有 $j< ...

  9. BZOJ1491 洛谷2047 NOI2007 社交网络

    Description: 在社交网络(social network)的研究中,我们常常使用图论概念去解释一些社会现象.不妨看这样的一个问题.在一个社交圈子里有n个人,人与人之间有不同程度的关系.我 们 ...

随机推荐

  1. Codeforces 1154B Make Them Equal

    题目链接:http://codeforces.com/problemset/problem/1154/B 题意:给定数组,可以给任意的的元素加上D 或者 减去D,如果能 使数组元素都相等,输出最小的D ...

  2. AT指令集之Call

    1.//unsolicited result code,URC表示BP->AP+ESIPCPI:<call_id>,<dir>,<sip_msg_type>, ...

  3. 17-Ubuntu-文件和目录命令-切换目录-相对路径和绝对路径

    1.相对路径: 在输入路径时,最前面不是/或者~,表示相对当前目录所在的目录位置. 例:当前桌面目录下,通过相对路径切换到桌面目录下的Entertainment目录 2.绝对路径: 在输入路径时,最前 ...

  4. vba增删改查数据库

    你在EXCEL中增加一个列名为ID,后在VBA中写以下代码,并引用Microsoft ActiveX Data Objects 2.8后执行Public Sub 写入SQL2008()Dim cnn ...

  5. C++ 系列:交换两个数字

    1. 创建中间变量 这是最快也是最简单的办法,例如: #include<stdio.h> int main(){ int a=10; int b=20; int temp; printf( ...

  6. [190308]Ubuntu 安装完之后,安装的软件小记

    install software vim sudo apt-get install -y vim Typora command copy from Typora website # or run: # ...

  7. Centos 6.5 python版本升级到2.7.8

    Centos6.5默认的 python版本是2.6 为了使用aliyuncli工具,直接用pip安装aliyuncli提示错误. 所以决定升级下python版本,但是yum依赖于python2.6,升 ...

  8. redis笔记_源码_跳表skiplist

    参照:https://juejin.im/post/57fa935b0e3dd90057c50fbc#comment http://redisbook.com/preview/skiplist/dat ...

  9. css样式初始化代码总结

    编写css样式之前需要初始化css样式,总结如下: /* CSS Document */ html, body, div, span, object, iframe,h1, h2, h3, h4, h ...

  10. leetcode-第10周双周赛-5080-查找两颗二叉搜索树之和

    题目描述: 自己的提交: class Solution: def twoSumBSTs(self, root1: TreeNode, root2: TreeNode, target: int) -&g ...