发现这个重开操作十分鬼畜,活生生将每一个状态和初始状态连了一条边。
由于每次重开肯定是使得本次通过关卡的时间增多,所以最优的情况一定是这次一定必然比期望时间多时才重开,也就是说,最优的情况一定是重开时候的期望和最终的期望相同。
发现重开时候期望时间越大必然导致了最后通关时间变长,所以可以用二分答案加上一个 DP
解决。
设 (f_{i, j}) 表示现在在完成了前面 (i) 个任务点,已经花了 (j) 时间,还需要的时间期望值,有转移:
[f_{i, j} = p_{i + 1} imes (f_{i + 1, j + F_{i + 1}} + F_{i + 1}) + (1 - p_{i + 1}) imes (f_{i + 1, j + S_{i + 1}} + S_{i + 1})
]
重开的转移只在 (i > 0) 时存在:
[f_{i, j} = min(f_{i, j}, mid)
]
其中 (mid) 是二分的答案。
时间复杂度 (mathcal O (n imes T imes log A))
#include <bits/stdc++.h>
#define forn(i,s,t) for(register int i=(s); i<=(t); ++i)
#define form(i,s,t) for(register int i=(s); i>=(t); --i)
#define rep(i,s,t) for(register int i=(s); i<(t); ++i)
using namespace std;
typedef double f64;
const int N = 103, M = 100 * 103;
int n, m, F[N], S[N], p[N], pre[N], sumF, sumS; f64 f[N][M];
inline bool check(f64 mid) {
memset(f, 0, sizeof f);
forn(i,m + 1,sumS) f[n][i] = mid;
form(i,n - 1,0) forn(j,0,pre[i]) {
f[i][j] = 1.0 * p[i + 1] / 100 * (f[i + 1][j + F[i + 1]] + F[i + 1]) + 1.0 * (100 - p[i + 1]) / 100 * (f[i + 1][j + S[i + 1]] + S[i + 1]);
if(i) f[i][j] = min(f[i][j], mid);
}
return f[0][0] <= mid;
}
int main() {
scanf("%d%d", &n, &m);
forn(i,1,n) scanf("%d%d%d", F + i, S + i, p + i), sumF += F[i], sumS += S[i], pre[i] = pre[i - 1] + S[i];
f64 l = 0, r = 1e8, res = 1e3, mid;
while(r - l > 1e-8) {
mid = (l + r) * 0.5;
if(check(mid)) r = mid, res = mid;
else l = mid;
}
printf("%.7lf
", res);
return 0;
}