货币兑换

本文探讨了一种解决决策点优化问题的方法,通过一次函数法来避免负数困扰。作者指出,传统的决策点比较法在处理某些情况时可能失效,而一次函数法通过维护斜率递增的凸包解决了这一问题。文章通过代码示例展示了如何应用此方法,并强调了在斜率优化中理解核心概念的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.前言

这道题不难,但是要对知识点掌握的比较熟练,理解比较透彻(或者说这才是难点?)

二.题解

1.郭老师的决策点比较方法

郭老师讲的决策点方法通俗易懂,能及其节省脑细胞的解决许多斜率优化的题,但是会遇到负数的困扰,例如这道题,若用郭老师的办法,则会推出下列式子


状态转移方程

num[j]=dp[j]/(a[j].a∗a[j].Rate+a[j].b);num[j] = dp[j] / (a[j].a * a[j].Rate + a[j].b);num[j]=dp[j]/(a[j].aa[j].Rate+a[j].b);

dp[i]=Max(dp[i],num[j]∗a[j].Rate∗a[i].a+num[j]∗a[i].b)dp[i] = Max (dp[i], num[j] * a[j].Rate * a[i].a + num[j] * a[i].b)dp[i]=Max(dp[i],num[j]a[j].Ratea[i].a+num[j]a[i].b)


j<kj < kj<k wj<wkw_j < w_kwj<wkwjw_jwj表示选择jjj决策点的效益)

num[j]∗a[j].Rate∗a[i].a+num[j]∗a[i].bnum[j] * a[j].Rate * a[i].a + num[j] * a[i].bnum[j]a[j].Ratea[i].a+num[j]a[i].b <<< num[k]∗a[k].Rate∗a[i].a+num[k]∗a[i].bnum[k] * a[k].Rate * a[i].a + num[k] * a[i].bnum[k]a[k].Ratea[i].a+num[k]a[i].b

a[i].a∗(num[j]∗a[j].Rate−num[k]∗a[k].Rate)<a[i].b∗(num[k]−num[j])a[i].a * (num[j] * a[j].Rate - num[k] * a[k].Rate) < a[i].b * (num[k] - num[j])a[i].a(num[j]a[j].Ratenum[k]a[k].Rate)<a[i].b(num[k]num[j])

num[j]∗a[j].Rate−num[k]∗a[k].Rate>0num[j] * a[j].Rate - num[k] * a[k].Rate > 0num[j]a[j].Ratenum[k]a[k].Rate>0a[i].aa[i].b<num[k]−num[j]num[j]∗a[j].Rate−num[k]∗a[k].Rate\frac{a[i].a}{a[i].b}<\frac{num[k]-num[j]}{num[j]*a[j].Rate-num[k]*a[k].Rate}a[i].ba[i].a<num[j]a[j].Ratenum[k]a[k].Ratenum[k]num[j]

num[j]∗a[j].Rate−num[k]∗a[k].Rate<0num[j] * a[j].Rate - num[k] * a[k].Rate < 0num[j]a[j].Ratenum[k]a[k].Rate<0a[i].aa[i].b>num[k]−num[j]num[j]∗a[j].Rate−num[k]∗a[k].Rate\frac{a[i].a}{a[i].b}>\frac{num[k]-num[j]}{num[j]*a[j].Rate-num[k]*a[k].Rate}a[i].ba[i].a>num[j]a[j].Ratenum[k]a[k].Ratenum[k]num[j]

num[j]∗a[j].Rate−num[k]∗a[k].Rate<0num[j] * a[j].Rate - num[k] * a[k].Rate < 0num[j]a[j].Ratenum[k]a[k].Rate<0num[j]<num[k]num[j] < num[k]num[j]<num[k]

三个分类讨论,怎么去保存斜率?所以这个方法不可取了(但大部分题这种方法简单得多)

2.一次函数法

dp[i]=num[j]∗a[j].Rate∗a[i].a+num[j]∗a[i].bdp[i] = num[j] * a[j].Rate * a[i].a + num[j] * a[i].bdp[i]=num[j]a[j].Ratea[i].a+num[j]a[i].b

num[j]∗a[i].b=dp[i]−num[j]∗a[j].Rate∗a[i].anum[j] * a[i].b = dp[i] -num[j] * a[j].Rate * a[i].anum[j]a[i].b=dp[i]num[j]a[j].Ratea[i].a

num[j]=dp[i]−num[j]∗a[j].Rate∗a[i].aa[i].bnum[j] = \frac{dp[i] - num[j] * a[j].Rate * a[i].a}{a[i].b}num[j]=a[i].bdp[i]num[j]a[j].Ratea[i].a

num[j]=−a[i].aa[i].b∗num[j]∗a[j].Rate+dp[i]a[i].bnum[j] = -\frac{a[i].a}{a[i].b} * num[j]*a[j].Rate + \frac{dp[i]}{a[i].b}num[j]=a[i].ba[i].anum[j]a[j].Rate+a[i].bdp[i]

y=num[j]y = num[j]y=num[j] k=−a[i].aa[i].bk = -\frac{a[i].a}{a[i].b}k=a[i].ba[i].a x=num[j]∗a[j].Ratex = num[j]*a[j].Ratex=num[j]a[j].Rate b=dp[i]a[i].bb = \frac{dp[i]}{a[i].b}b=a[i].bdp[i]

y=kx+by = kx + by=kx+b, 老一次函数了

维护一个斜率递增的凸包即可

3.小结

(1).

决策点比较法无法处理:维护存在两对相邻点斜率乘积 ≤\leq 000的情况。
即如下图
在这里插入图片描述

(2).

有些时候我们的 xxx 并不是单调递增,所以不能用队列来维护,只能用 CDQCDQCDQ 或者其他我不会更高级的算法去维护。本题中用 CDQCDQCDQ 只需要将 xxx 按从小到大排序即可。
(这就是我为什么一直 WA 000

4.代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;

#define LL long long
template <typename T>
void read (T &x) {
    x = 0; T f = 1;
    char ch = getchar ();
    while (ch < '0' || ch > '9') {
        if (ch == '-') f = -1;
        ch = getchar ();
    }
    while (ch >= '0' && ch <= '9') {
        x = (x << 1) + (x << 3) + ch - '0';
        ch = getchar ();
    }
    x *+ f;
}
template <typename T>
T Max (T x, T y) { return x > y ? x : y; }
template <typename T>
T Min (T x, T y) { return x < y ? x : y; }
template <typename T>
T Abs (T x) { return x > 0 ? x : -x; }

const int Maxn = 1e5;
const double Inf = 0x7f7f7f7f7f7f;

int n, s;
double dp[Maxn + 5];

struct Date {
    double a, b, Rate;
}a[Maxn + 5];
int hh, tt;
int que[Maxn + 5];
double num (int j) { return dp[j] / (a[j].a * a[j].Rate + a[j].b); }
double Get_x (int j) { return num (j) * a[j].Rate; }
double Get_y (int j) { return num (j); }
double Get_dp (int i, int j) { return num (j) * a[j].Rate * a[i].a + num (j) * a[i].b; }
double Get_k (int i, int j) { return (Get_y (j) - Get_y (i)) / (Get_x (j) - Get_x (i)); }
void add (int x) {
    while (hh < tt && (Get_y (que[tt]) - Get_y (que[tt - 1])) * (Get_x (x) - Get_x (que[tt - 1])) <= (Get_y (x) - Get_y (que[tt - 1])) * (Get_x (que[tt]) - Get_x (que[tt - 1]))) {
        tt--;
    }
    que[++tt] = x;
}

int q[Maxn + 5];
bool cmpx (int x, int y) {
	return Get_x (x) < Get_x (y);
}
void CDQ (int l, int r) {
	if (l == r) return;
	int mid = (l + r) >> 1;
	
	CDQ (l, mid);
	sort (q + l, q + 1 + mid, cmpx);
	hh = 1; tt = 0; double _max = -Inf;
	for (int i = l; i <= mid; i++) {
		_max = Max (_max, dp[q[i]]);
		add (q[i]);
	}
	for (int i = mid + 1; i <= r; i++) {
        int L = hh, R = tt;
        while (L + 1 < R) {
            int mid = (L + R) >> 1;
            if (Get_dp (q[i], que[mid]) < Get_dp (q[i], que[mid + 1])) L = mid;
            else R = mid;
        }
        dp[q[i]] = Max (dp[q[i]], Get_dp (q[i], que[L]));
        dp[q[i]] = Max (dp[q[i]], Get_dp (q[i], que[R]));
        dp[q[i]] = Max (dp[q[i]], _max);
	}
	sort (q + l, q + 1 + r);
	CDQ (mid + 1, r);
}

int main () {
    read (n); read (s);
    for (int i = 1; i <= n; i++) {
        scanf ("%lf %lf %lf", &a[i].a, &a[i].b, &a[i].Rate);
    }
    
    dp[1] = s;
    for (int i = 1; i <= n; i++) q[i] = i;
    
    CDQ (1, n);
    
    double ans = s;
    for (int i = 1; i <= n; i++) ans = Max (ans, dp[i]);
    printf ("%.3lf", ans);
    return 0;
}

5.后记

这道题真的深深地打醒了我,我不得不重新审视了一下之前斜率优化中我囫囵吞枣的记类型过去的重点,当知识点出现较大变化时,便束手无措了。这道题是一个很好的教训。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值