洛谷P4027 [NOI2007]货币兑换
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]货币兑换的更多相关文章
- 洛谷 P4027 [NOI2007]货币兑换 解题报告
P4027 [NOI2007]货币兑换 题目描述 小 \(Y\) 最近在一家金券交易所工作.该金券交易所只发行交易两种金券:\(A\) 纪念券(以下简称 \(A\) 券)和 \(B\) 纪念券(以下简 ...
- LOJ 2353 & 洛谷 P4027 [NOI2007]货币兑换(CDQ 分治维护斜率优化)
题目传送门 纪念一下第一道(?)自己 yy 出来的 NOI 题. 考虑 dp,\(dp[i]\) 表示到第 \(i\) 天最多有多少钱. 那么有 \(dp[i]=\max\{\max\limits_{ ...
- 洛谷P4027 [NOI2007]货币兑换(dp 斜率优化 cdq 二分)
题意 题目链接 Sol 解题的关键是看到题目里的提示... 设\(f[i]\)表示到第\(i\)天所持有软妹币的最大数量,显然答案为\(max_{i = 1}^n f[i]\) 转移为\(f_i = ...
- P4027 [NOI2007]货币兑换(斜率优化dp+cdq分治)
P4027 [NOI2007]货币兑换 显然,如果某一天要买券,一定是把钱全部花掉.否则不是最优(攒着干啥) 我们设$f[j]$为第$j$天时用户手上最多有多少钱 设$w$为花完钱买到的$B$券数 $ ...
- 洛谷 P2047 [NOI2007]社交网络 解题报告
P2047 [NOI2007]社交网络 题目描述 在社交网络(\(social\) \(network\))的研究中,我们常常使用图论概念去解释一些社会现象.不妨看这样的一个问题.在一个社交圈子里有\ ...
- 洛谷——P2047 [NOI2007]社交网络
P2047 [NOI2007]社交网络 $Floyd$,一眼看到就是他(博主是不小心瞄到了这个题的标签吧qwq) 这个题目只要预处理出$S$到$T$的最短路的条数即可,类似$Spfa$的更新方法 如果 ...
- 洛谷P2047 [NOI2007]社交网络 [图论,最短路计数]
题目传送门 社交网络 题目描述 在社交网络(social network)的研究中,我们常常使用图论概念去解释一些社会现象.不妨看这样的一个问题.在一个社交圈子里有n个人,人与人之间有不同程度的关系. ...
- P4027 [NOI2007]货币兑换
传送门 首先有一个显然的贪心,每次操作都要做到底,为了最优不会出现只卖一部分或者只买一部分的操作 所以设 $f[i]$ 表示前 $i$ 天得到的最大价值,那么对于每一个 $i$,枚举所有 $j< ...
- BZOJ1491 洛谷2047 NOI2007 社交网络
Description: 在社交网络(social network)的研究中,我们常常使用图论概念去解释一些社会现象.不妨看这样的一个问题.在一个社交圈子里有n个人,人与人之间有不同程度的关系.我 们 ...
随机推荐
- 拾遗:Unix 守护进程编写规范
//标准库自带函数,通常以 daemon(0, 0) 方式调用 int daemon(int nochdir, int noclose) Linux: #include <unistd.h> ...
- Vue的项目搭建及请求生命周期
目录 Vue的项目搭建及请求生命周期 Vue-CLI的项目搭建 环境搭建 项目创建 pycharm运行Vue项目 Vue项目的大体结构 Vue的请求生命周期 两个小用法 Vue的项目搭建及请求生命周期 ...
- importError:cannot import name imsave/imread等模块
首先要先看相应的库是否已经安裝成功 pip install numpy pip install pillow pip install scipy 都成功安装之后,执行: import scipy.mi ...
- HTML_页面注册案例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 搭建HDFS HA
搭建HDFS HA 1.服务器角色规划 hd-01(192.168.1.99) hd-02 (192.168.1.100) hd-03 (192.168.1.101) NameNode NameNod ...
- sql(11) sum
合计函数 (比如 SUM) 常常需要添加 GROUP BY 语句. GROUP BY 语句GROUP BY 语句用于结合合计函数,根据一个或多个列对结果集进行分组.新建表 StudentSS_id G ...
- gradle 随记
gradle项目下添加jar包 compile fileTree(dir: './src/main/resources/lib', include: '*.jar') 将jar包放到这个目录下./sr ...
- 0908CSP-S模拟测试赛后总结
我早就料到昨天会考两场2333 话说老师终于给模拟赛改名了啊. 距离NOIP祭日还有60天hhh. 以上是废话. %%%DeepinC无敌神 -rank1 zkt神.kx神.动动神 -rank2 有钱 ...
- LUOGU P2860 [USACO06JAN]冗余路径Redundant Paths (双联通,缩点)
传送门 解题思路 刚开始是找的桥,后来发现这样不对,因为一条链就可以被卡.后来想到应该缩点后找到度数为1 的点然后两两配对. #include<iostream> #include< ...
- AFO成功
在dcoi一年多,还是发生了不少事情. 过程中也有些小遗憾,有做错的事情,有搞砸的事情,有没办法挽回的事情,这种没法读档的辣鸡游戏也是无可奈何的.对所有被我搞砸的事情说声对不起啦,至少在下一次的时候, ...