CF865C Gotta Go Fast 题解
考虑二分答案然后 dp,钦定 \(mid\) 为重新开始的期望时间。若求出的期望时间 \(<mid\) 小,则答案 \(<mid\)。
记 \(f_{i,j}\) 表示当前到了第 \(i\) 关,用了 \(j\) 单位时间通关需要花费的的期望总时间。考虑怎么转移:
- 若 \(j>R\),则必须重启,\(f_{i,j}=j+mid\)。
- 若 \(j\le R\),则 \(f_{i,j}=\min(j+mid,\frac{p_i}{100}f_{i+1,j+a_i}+(1-\frac{p_i}{100})f_{i+1,j+b_i})\)。
比较 \(f_{1,0}\) 与 \(mid\) 即可。时间复杂度 \(\mathcal O(nR\log V)\)。
参考代码:
#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define rept(i,a,b) for(int i=(a);i<(b);++i)
#define drep(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
int n,s,sm,a[53],b[53],p[53];
long double l,r,f[53][5003];
bool check(long double mid){
rep(i,0,sm)f[n+1][i]=i>s?i+mid:i;
drep(i,n,1){
rep(j,0,sm){
f[i][j]=j>s?j+mid:min(j+mid,p[i]/100.0*f[i+1][j+a[i]]+(1-p[i]/100.0)*f[i+1][j+b[i]]);
}
}
return f[1][0]<mid;
}
signed main(){
scanf("%d%d",&n,&s);
rep(i,1,n)scanf("%d%d%d",&a[i],&b[i],&p[i]),sm+=b[i];
r=1e9;
while(l+1e-11<r){
long double mid=(l+r)/2;
if(check(mid))r=mid;
else l=mid;
}
printf("%.10Lf",l);
return 0;
}

浙公网安备 33010602011771号