[期望计算+二分]2020-2021 ICPC Northwestern European Regional Programming Contest (NWERC)Great Expectation

link

题面

  这题有点像玩QQ飞车。有一个赛道记录时间为 r r r,赛道上有 m m m 个可能失误点,如果完全没有失误完成赛道的时间是 n ( n < r ≤ 5000 ) n (n < r ≤ 5000) n(nr5000)。 在完全没有失误的情况下,通过第 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 22+192=21

分析

   记可以失误的秒数 m a r g i n = r − d − 1 margin = r - d - 1 margin=rd1,那么失误掉的时间不能超过这个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])+(1p[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])+(1p[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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值