题目
Roll or Increment 地址:at_abc224_G
解析
-
TAG: 数学,概率和期望
-
DESCRIPITION:
N N N 面的骰子,求从初始点数 S S S 操作到目标点数 T T T 的期望代价,有两种操作对应两个操作代价。
op1: 花费代价 A A A另点数加一。
op2: 花费代价 B B B随机投一个点数。 -
SOLUTION:
(1)步骤1:分别分析代价
先分别分析一下只使用一种操作到目标点数的代价,设当前点数为 i i i,我们的目标点数为 T T T。操作1: 仅仅当 i ≤ T i≤T i≤T,代价为 ( T − i ) ⋅ A (T-i)·A (T−i)⋅A,可以理解为 i i i 从小到大离 T T T 的距离乘 A A A 就是op1的代价,设置这个距离再加上 T T T 点本身为 L = T − i + 1 L = T - i + 1 L=T−i+1 。
所以代价为 ( T − i ) ⋅ A = ( L − 1 ) ⋅ A (T-i)·A = (L-1)·A (T−i)⋅A=(L−1)⋅A 。
操作2: 由于随机扔骰子全看概率,这里的代价只能求一个平均代价: N ⋅ B N·B N⋅B 。(2)结合起来分析最小代价
前面我们做了分别分析代价的工作,但实际上我们要做的是一个不断循环迭代分析的工作,所以需要两个操作结合起来看。
那我们在当前点数 i i i 的基础上时再做操作选择时,就要判断 ( L − 1 ) ⋅ A ≤ N ⋅ B (L-1)·A ≤ N·B (L−1)⋅A≤N⋅B 是否成立,若成立就说明操作1更优先。
于是,我们得到一个区间 ( T − L , T ] (T-L,T] (T−L,T] ,点数在区间内则可以直接循环op1就结束了,区间外则先op2,再根据随机点数位置判断。
得到最小后续期望
f ( i ) = { ( L − 1 ) ⋅ A T-L<i≤T, X else. f(i)= \begin{cases} (L-1)·A & \text{T-L<i≤T,}\\ X& \text{else.} \end{cases} f(i)={(L−1)⋅AXT-L<i≤T,else.
其中后续期望 X = B + N − L N ⋅ X + 1 N ⋅ ∑ k = 0 L − 1 k A X = B + \frac{N-L}{N} ·X+\frac{1}{N}· \sum\limits_{k=0}^{L-1}{kA} X=B+NN−L⋅X+N1⋅k=0∑L−1kA(3)分析区间外时后续期望X与L关系
解方程得: X = N ⋅ B L + A ⋅ ( L − 1 ) 2 X = \frac{N·B}{L} +\frac{A·(L-1)}{2} X=LN⋅B+2A⋅(L−1)
求导算极值: L = 2 B N A L = \sqrt{\frac{2BN}{A}} L=A2BN,此时 X X X 最小
(4)比较一下结合求解后的 X X X与直接利用op1算出来的答案输出最小值即可
参考源码
#include<iostream>
#include<cmath>
#include<iomanip>
using namespace std;
typedef long long ll;
ll N, S, T, A, B;
double X_L(ll L) {
return A * (L - 1) / 2.0 + B * N * 1.0/ L;
}
int main() {
cin >> N >> S >> T >> A >> B;
ll L = sqrt(2.0 * B * N / A);
double res = 1e18;
//if (T >= S) res = min(res, A *1.0* (T - S)); // 区间内时期望
if (T >= S) res = A *1.0* (T - S); // 区间内时期望
// minx可能不是整数,算一下边缘整数
for (ll i = L-1; i <= L+1; i++) {
if (i >= 1 && i <= T) // 边界情况,i=1时相当于不执行方案1一直重扔,i=x时相当于扔到T左边就一直加1
res = min(res, X_L(i)); // 与区间外内期望比较
}
cout<<fixed<<setprecision(8);
cout << res << endl;
return 0;
}
代码参考博客https://blog.csdn.net/cmershen/article/details/120930761