【期望dp练习】lyk loves jumping

没有提交地址

题目描述

LYK 发现了 n 个点,它想在这些点上跳来跳去。
具体地,第 i 个点有一个高度 hi ,当 LYK 踩在这个点上方时,这个点会下降 ti ,也就是说此时该点高度为 hiti ,当 LYK 离开该点时,这个点又会恢复到原来的 hi
现在 LYK 每次可以跳到不超过它当前所在点高度的点(当 ti =0 时,LYK 可以跳到自己所在的点) ,若存在多个这样的点,则 LYK 等概率随机挑选一个点并跳到那个点,若不存在这样的点,则 LYK 会跳到地面。
求 LYK 以第 i 个点为起点时,期望跳几次能够跳到地面(保留 3 位有效数字) 。
若期望步数为无穷,输出 0.000。
表示无穷大,X 为一个数,有 -X= *X= /X= +X=

输入格式

第一行输入一个数 n,表示有 n 个点。 第二行输入 n 个数,表示 hi 。 第三行输入 n 个数,表示 ti

输出格式

输出一行 n 个数,表示以当前点为起点时,期望跳几次跳到地面(保留 4 位小数) ,若期望次数为无穷,输出“0.000” 。

样例输入

4
4 2 2 3
0 1 0 0

样例输出

3.8333 1.0000 3.0000 3.5000

数据范围

对于 20%的数据 n5
对于另外 20%的数据所有 hi 都相等。
对于再另外 20%的数据不存在 ti =0。
对于再再另外 20%的数据 hi 都互不相等。
对于 100%的数据 1n,hi105 0tihi

思路:

首先按高度的升序排序,把 ti=0 hi 相同的提取出来另算,每次二分找出可以跳的区间,记录答案。
dp[i] 表示 i 跳出去的期望,每次转移时,可以跳的有a个, ti=0 hi 相同的有 b 个。

ti0时:

  • a=0 时: dp[i]=1
  • 否则: dp[i]=aj=1dp[j]a+1

ti=0 时:

  • ab=0 时: dp[i]=inf
  • 否则: dp[i]=abj=1dp[j]ab+aab

代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const double eps = 0.00000001;
const double inf = 1e12;
struct node{
    int h, t, b, pos;
    double res;
    bool operator < (const node &tt)const{
        return h == tt.h ? (t > tt.t) : (h < tt.h);
    }
    void operator = (const node &tt){
        h = tt.h;
        t = tt.t;
        b = tt.b;
        pos = tt.pos;
        res = tt.res;
    }
};
bool cmp(node a, node b){
    return a.pos < b.pos;
}
node p0[100010], p[100010];
double dp[100010], sum2[100010];
int sum1[100010], n, cnt;
int find(int l, int r, int val){
    int res = 0;
    while(l <= r){
        int mid = (l+r)>>1;
        if(p[mid].h <= val) res = mid, l = mid + 1;
        else r = mid - 1; 
    }
    return res;
}
int main(){
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++) scanf("%d", &p0[i].h);
    for(int i = 1; i <= n; i ++) scanf("%d", &p0[i].t), p0[i].pos = i;
    sort(p0+1, p0+1+n);
    for(int i = 1; i <= n; i ++){
        if(p0[i].h == p0[i-1].h && p0[i].t == p0[i-1].t) p[cnt].b ++;
        else p[++cnt] = p0[i], p[cnt].b = 1;
    } 
    for(int i = 1; i <= cnt; i ++){
        sum1[i] = sum1[i-1] + p[i].b;
        int pos = find(1, i, p[i].h-p[i].t);
        double a = sum1[pos];
        if(p[i].t != 0){
            if(a == 0) p[i].res = 1;
            else p[i].res = (sum2[pos]/a)+1;
        }else{
            if(a-p[i].b == 0) p[i].res = -inf;
            else p[i].res = (sum2[pos-1]/(a-p[i].b))+(a/(a-p[i].b));
        }
        sum2[i] = p[i].b*p[i].res + sum2[i-1];
    }
    for(int i = 1, pp = 0; i <= n; i ++){
        if(p0[i].h == p0[i-1].h && p0[i].t == p0[i-1].t) ;
        else pp ++;
        p0[i].res = p[pp].res;
    }
    sort(p0+1, p0+1+n, cmp);
    for(int i = 1; i <= n; i ++) printf("%.3lf ", p0[i].res <= 0 ? (0.000) : (p0[i].res));
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值