洛谷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个人,人与人之间有不同程度的关系.我 们 ...
随机推荐
- POJ2406-Power Strings-KMP循环节/哈希循环节
Given two strings a and b we define a*b to be their concatenation. For example, if a = "abc&quo ...
- Codeforces 1166B - All the Vowels Please
题目链接:http://codeforces.com/problemset/problem/1166/B 个元音. 思路:先判断能否弄出至少5*5的行列,然后按顺序填字符串就好了. AC代码: #in ...
- 《Java语言程序设计》编程练习6.18(检测密码)
6.18 (检测密码)一些网站对于密码具有一些规则.编写一个方法,检测字符串是否是一个有效密码. 假定密码规则如下: • 密码必须至少8位字符. • 密码仅能包含字母和数字. ...
- USACO 2008 January Silver Telephone Lines /// 二分最短路 邻接表dijkstra oj22924
题目大意: 一共有N (1 ≤ N ≤ 1,000)个电线杆,有P P (1 ≤ P ≤ 10,000)对电线杆是可以连接的, 用几条线连接在一起的电线杆之间都可相互通信,现在想要使得电线杆1和电线杆 ...
- asp.net Core 使用redis(StackExchange.Redis)
原文:asp.net Core 使用redis(StackExchange.Redis) 一.添加配置(appsettings.json) "Redis": { "Def ...
- EXCEL表格链接SQLSEVER数据库
Sub 数据库连接() Set Cnn = CreateObject("ADODB.Connection") Set rs = CreateObject(" ...
- python日常使用
os.path.splitext('C:\py\wxPython.gif') 得到扩展名的函数 os.remove(删除文件) os.listdir(显示该目录下的文件) os.getcwd(获取当 ...
- 数据库MySQL--修改数据表
创建数据库::create database 数据库名: 如果数据不存在则创建,存在不创建:Create database if not exists 数据库名 ; 删除数据库::drop datab ...
- mysql 监控及优化——转载自http://www.cnblogs.com/suansuan/
1.Mysql连接数 Mysql默认最大连接数为100. 设置Mysql的最大连接数,在Mysql的配置文件中增加: max_connections = 1000 #Mysql的最大连接数,默认如 ...
- MySQL回滚到某一时刻数据的方法
MySQL回滚到某一时刻数据的方法 对于有归档日志的数据库来说,原理上都具备全库回滚到之前某一时刻的能力.在这方面最好用的Orale数据库,使用Oracle数据库的RMAN工具,可以方便的 ...