D. Gambler’s Ruin
题目大意
需要设置两个赔率x和y。使得求出一个最佳的赔率,使得赌博公司利润最大
使得
[
s
x
+
s
y
−
m
a
x
(
s
x
∗
x
,
s
y
∗
y
)
]
最大
使得 [s_x + s_y - max(s_x *x,s_y *y)] 最大
使得[sx+sy−max(sx∗x,sy∗y)]最大
题解思维
当某个(赌徒) p i 的赔率满足 p i ∗ x > = 1 时,也就是 x > = 1 p i 时 , 赌徒会下注 c i 当某个(赌徒)p_i 的赔率满足p_i *x >=1时,也就是x>=\frac{1}{p_i}时,赌徒会下注c_i 当某个(赌徒)pi的赔率满足pi∗x>=1时,也就是x>=pi1时,赌徒会下注ci
另一个则是 y 的,同理得到, y > = 1 1 − p i 时 , 赌徒会下注 c i 另一个则是y的,同理得到,y>=\frac{1}{1-p_i}时,赌徒会下注c_i 另一个则是y的,同理得到,y>=1−pi1时,赌徒会下注ci
由于 0 < = p i < = 1 ,所以当 p i 越来越小时, 1 p i 越来越大,所以我们需要对输入的数据根据 p i 进行排序【本文中使用了降序排序】 由于0<=p_i<=1,所以当pi越来越小时,\frac{1}{pi}越来越大,所以我们需要对输入的数据根据p_i 进行排序【本文中使用了降序排序】 由于0<=pi<=1,所以当pi越来越小时,pi1越来越大,所以我们需要对输入的数据根据pi进行排序【本文中使用了降序排序】
假设排序完成后的数组,中元素是以 a 0 , a 1 , ⋅ ⋅ ⋅ , a n ,这样的数组 假设排序完成后的数组,中元素是以a_0,a_1,···,a_n,这样的数组 假设排序完成后的数组,中元素是以a0,a1,⋅⋅⋅,an,这样的数组
如果有 p i 相同的元素,可以将它们合并 如果有p_i 相同的元素,可以将它们合并 如果有pi相同的元素,可以将它们合并
我们假设设置了 1 a 4 为赔率,那么在它【 a 4 】之前的元素,显然都满足下注的条件 我们假设设置了\frac{1}{a_4}为赔率,那么在它【a_4】之前的元素,显然都满足下注的条件 我们假设设置了a41为赔率,那么在它【a4】之前的元素,显然都满足下注的条件
由于 y 是 1 − p i 关联的,显然和 x 相反,也就是,当认定 1 1 − a j 时, a 之 后的都满足条件 由于y是1-p_i关联的,显然和x相反,也就是,当认定\frac{1}{1-aj}时,a_之后的都满足条件 由于y是1−pi关联的,显然和x相反,也就是,当认定1−aj1时,a之后的都满足条件
问题就简化成了,找到 a i , a j 使得,使得赌博公司利润最大 问题就简化成了,找到a_i,a_j使得,使得赌博公司利润最大 问题就简化成了,找到ai,aj使得,使得赌博公司利润最大
对于 s x ∗ x ,其实可以理解为,这个排序好后的前缀和和 ( 1 a i ) 的乘积 = v 1 = ( 1 a i ) ∗ ∑ i = 1 i c i 对于s_x *x,其实可以理解为,这个排序好后的前缀和和(\frac{1}{a_i})的乘积=v1 =(\frac{1}{a_i})*\sum_{i=1}^{i}{c_i} 对于sx∗x,其实可以理解为,这个排序好后的前缀和和(ai1)的乘积=v1=(ai1)∗i=1∑ici
对于 s y ∗ y ,其实可以理解为,这个排序好后的后缀和和 ( 1 1 − a j ) 的乘积 = v 2 = ( 1 1 − a j ) ∗ ∑ i = j n − j c i 对于s_y *y,其实可以理解为,这个排序好后的后缀和和(\frac{1}{1-a_j})的乘积=v2 =(\frac{1}{1-a_j})*\sum_{i=j}^{n-j}{c_i} 对于sy∗y,其实可以理解为,这个排序好后的后缀和和(1−aj1)的乘积=v2=(1−aj1)∗i=j∑n−jci
发现,当i,j越来越大时,v1是越来越大,v2越来越小
由于存在
m
a
x
(
s
x
∗
x
,
s
y
∗
y
)
,
那么显然,当固定
i
或
j
时,另外一个的变化会有一个零界点,当它越过零界点时,
m
a
x
的值会产生变化
由于存在max(s_x *x,s_y *y),那么显然,当固定i或j时,另外一个的变化会有一个零界点,当它越过零界点时,max的值会产生变化
由于存在max(sx∗x,sy∗y),那么显然,当固定i或j时,另外一个的变化会有一个零界点,当它越过零界点时,max的值会产生变化
假设固定了某个 a i , 使得 S x ∗ x 固定,假设 p 从数组 a n 开始, a j 是从 a n 开始倒着的遍历 , 希望找到第一个 a j 使得它大于或等于 S x ∗ x 假设固定了某个a_i,使得Sx*x固定,假设p从数组a_n开始,a_j是从a_n开始倒着的遍历,希望找到第一个a_j使得它大于或等于Sx*x 假设固定了某个ai,使得Sx∗x固定,假设p从数组an开始,aj是从an开始倒着的遍历,希望找到第一个aj使得它大于或等于Sx∗x
假设这个 a j 的索引是 p , 那么当固定 a i ( s x ∗ x )时,所能求得的最大利润显然,在 p 时 [ 大的是 y ] ,或者 p + 1 时【大的是 x 】产生。 假设这个a_j 的索引是 p ,那么当固定a_i(s_x*x)时,所能求得的最大利润显然,在p时[大的是y],或者p+1时【大的是x】产生。 假设这个aj的索引是p,那么当固定ai(sx∗x)时,所能求得的最大利润显然,在p时[大的是y],或者p+1时【大的是x】产生。
有了上面的思路之后,就可以进行程序的编写了了,程序如下
#include<bits/stdc++.h>
using namespace std;
int n;
#define ll long long
const int lim = 1e6+10;
const double eps = 1e-10;
pair<double, ll> a[lim];
ll pres[lim], sufs[lim];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr), cout.tie(nullptr);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i].first>>a[i].second;
}
sort(a+1,a+n+1,greater<pair<double,ll>>());
int cnt=0;
for(int i=1;i<=n;i++)
{
if(i==1||fabs(a[i].first - a[i - 1].first) > eps)
a[++cnt] = a[i];
else
a[cnt].second += a[i].second;
}
n = cnt;
for (int i = 1; i <= n; ++i) pres[i] = pres[i - 1] + a[i].second;
for (int i = n; i; --i) sufs[i] = sufs[i + 1] + a[i].second;
double ans = 0;
//找到临界点,并将它和答案进行更新
for (int i = 1, p = n; i <= n; ++i) {
if (i == n || fabs(a[i].first - a[i + 1].first) > eps)
while (p > i && pres[i] / a[i].first > sufs[p] / (1 - a[p].first)) --p;
ans = max(ans, pres[i] + sufs[p + 1] - pres[i] / a[i].first);
if (i >= p) break;
ans = max(ans, pres[i] + sufs[p] - sufs[p] / (1 - a[p].first));
}
cout << fixed << setprecision(10) << ans << '\n';
}