link
题面
这题有点像玩QQ飞车。有一个赛道记录时间为
r
r
r,赛道上有
m
m
m 个可能失误点,如果完全没有失误完成赛道的时间是
n
(
n
<
r
≤
5000
)
n (n < r ≤ 5000)
n(n<r≤5000)。 在完全没有失误的情况下,通过第
i
i
i 个点的时间是
t
i
t_i
ti。对于第
i
i
i 个点,有
p
i
p_i
pi 的概率会成功通过,如果失败的话,则会耗时
d
i
d_i
di (额外增加的时间)。然后你还有一个reset选择,就是让赛车回到起点,计时时间重新开始。问为了打破纪录(记录的时间不算reset之前的时间),最佳策略所需花费时间的期望值。(这个时间包括reset之前的时间,即花费总时间)。
举个例子,比如
n
=
19
,
r
=
20
n = 19, r = 20
n=19,r=20, 只有一个可能失误点
t
=
10
,
p
=
0.5
,
d
=
1
t = 10, p = 0.5, d = 1
t=10,p=0.5,d=1, 如果在唯一的这个点失误了,那么完成赛道时间就是
19
+
1
=
20
19 + 1 = 20
19+1=20,破不了记录。所以最优策略只能是失误了就reset, 不失误就直接完成,那么这个期望模型就是几何概型了,期望次数是
1
/
p
=
2
1 / p = 2
1/p=2 次,那么所求时间就是
2
∗
2
+
19
−
2
=
21
2 * 2 + 19 - 2 = 21
2∗2+19−2=21。
分析
记可以失误的秒数
m
a
r
g
i
n
=
r
−
d
−
1
margin = r - d - 1
margin=r−d−1,那么失误掉的时间不能超过这个margin。这个margin的状态加上每个失误点,就可以进行动态规划维护状态了(状态不是很好想)。
记
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 表示刚好到达第
i
i
i 关准备通过,浪费了
j
j
j 秒情况下,破纪录还需要的最佳期望时间。 可以在起点和终点分别增加第 0 关和第 m + 1 关,通过概率为1,时间分别为0 和 n,这样有利于dp的转移。
如果
j
+
d
[
i
]
≤
m
a
r
g
i
n
j + d[i] ≤ margin
j+d[i]≤margin,那么可以得到转移方程(分为失败和不失败,失败又可以分为reset和继续走)
d
p
[
i
]
[
j
]
=
p
[
i
]
∗
(
t
[
i
+
1
]
−
t
[
i
]
+
d
p
[
i
+
1
]
[
j
]
)
+
(
1
−
p
[
i
]
)
∗
m
i
n
(
d
p
[
0
]
[
0
]
,
t
[
i
+
1
]
−
t
[
i
]
+
d
[
i
]
+
d
p
[
i
+
1
]
[
j
+
d
[
i
]
]
)
;
dp[i][j] = p[i] * (t[i + 1] - t[i] + dp[i + 1][j])+ (1 - p[i]) * min(dp[0][0], t[i + 1] - t[i] + d[i] + dp[i + 1][j + d[i]]);
dp[i][j]=p[i]∗(t[i+1]−t[i]+dp[i+1][j])+(1−p[i])∗min(dp[0][0],t[i+1]−t[i]+d[i]+dp[i+1][j+d[i]]);
如果
j
+
d
[
i
]
>
m
a
r
g
i
n
j + d[i] > margin
j+d[i]>margin,那么可以得到转移方程(分为失败和不失败,失败只能reset):
d
p
[
i
]
[
j
]
=
p
[
i
]
∗
(
t
[
i
+
1
]
−
t
[
i
]
+
d
p
[
i
+
1
]
[
j
]
)
+
(
1
−
p
[
i
]
)
∗
d
p
[
0
]
[
0
]
dp[i][j] = p[i] * (t[i + 1] - t[i] + dp[i + 1][j])+ (1 - p[i]) * dp[0][0]
dp[i][j]=p[i]∗(t[i+1]−t[i]+dp[i+1][j])+(1−p[i])∗dp[0][0]
从以上式子可以看出,我们要倒着dp, 最后要求的就是
d
p
[
0
]
[
0
]
dp[0][0]
dp[0][0],但是每个dp转移都和
d
p
[
0
]
[
0
]
dp[0][0]
dp[0][0]有关,即我们要求的值会先被用到。
可以合理猜测,如果我们任意初始化一个
d
p
[
0
]
[
0
]
dp[0][0]
dp[0][0] 用来dp迭代,算出来的
d
p
[
0
]
[
0
]
dp[0][0]
dp[0][0] 比初始值大,那么一开始的给小了,如果算出来的比初始值小,那么一开始的给大了,这规律符合二分条件。
所以就进行二分,答案可能会很大,所以上边界取的尽量大些。
#include <bits/stdc++.h>
#define here printf("modassing [%s] in LINE %d\n", __FUNCTION__, __LINE__);
#define debug(x) cout << #x << ":\t" << (x) << endl;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> P;
const int maxn = 2e3 + 10;
const int maxm = 2e6 + 10;
const int INF = 0x3f3f3f3f;
const ll mod = 1e9 + 7;
const double pi = acos(-1.0);
const double eps = 1e-8;
int n, r, m, t[60], d[60], margin;
long double p[60];
long double dp[60][5010];
long double solve(long double x)
{
for (int i = m; i >= 0; i--)
{
for (int j = 0; j <= margin; j++)
{
dp[i][j] = p[i] * (t[i + 1] - t[i] + dp[i + 1][j]);
if(j + d[i] > margin)
dp[i][j] += (1 - p[i]) * x;
else
dp[i][j] += (1 - p[i]) * min(x, t[i + 1] - t[i] + d[i] + dp[i + 1][j + d[i]]);
}
}
return dp[0][0];
}
int main()
{
scanf("%d %d %d", &n, &r, &m);
for (int i = 1; i <= m; i++)
scanf("%d %Lf %d", &t[i], &p[i], &d[i]);
t[0] = 0;
p[0] = 1;
t[m + 1] = n;
p[m + 1] = 1;
margin = r - n - 1;
long double l = 0, r = 3e18, mid, tmp;
while(r - l > eps)
{
mid = (l + r) / 2;
tmp = solve(mid);
if(tmp > mid) l = mid;
else r = mid - eps;
}
printf("%.8Lf\n", l);
}