• 【bzoj3029】守卫者的挑战 概率dp


    题目描述

    给出一个数$m$和$n$次操作,第$i$操作有$p_i$的概率成功,成功后会使$m$加上$a_i$($a_i$为正整数或$-1$),求$n$次操作以后成功的操作次数不少于$l$且$mge 0$的概率。

    输入

    第一行三个整数N,L,M。
    第二行N个实数,第i个实数pi表示第i项挑战成功的百分比。
    第三行N个整数,第i个整数ai表示第i项挑战的属性值.

    输出

    一个整数,表示所求概率,四舍五入保留6 位小数。

    样例输入

    3 1 0
    10 20 30
    -1 -1 2

    样例输出

    0.300000


    题解

    概率dp

    设$f[i][j][k]$表示前$i$次操作成功了$j$次,此时$m$的值为$k$的概率。

    那么状态转移显然。

    然而有一个问题:$m(k)$的范围过大。

    考虑到$a_i$仅为正整数或$-1$,而最终只要求$mge 0$。当一个时刻$mge n$时,无论怎么减少都不会降到$0$以下。因此当$m>n$时直接将其看作$n$处理即可。

    数组下标需要向右平移$n$位。

    由于空间不足需要使用滚动数组。

    时间复杂度$O(n^3)$

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 210
    using namespace std;
    double p[N] , f[2][N][N << 1];
    int main()
    {
    	int n , t , m , a , i , j , k , d;
    	double ans = 0;
    	scanf("%d%d%d" , &n , &t , &m) , m = min(m , n);
    	for(i = 1 ; i <= n ; i ++ ) scanf("%lf" , &p[i]) , p[i] /= 100;
    	f[0][0][n + m] = 1;
    	for(d = i = 1 ; i <= n ; i ++ , d ^= 1)
    	{
    		scanf("%d" , &a);
    		for(j = 0 ; j <= n ; j ++ )
    			for(k = 0 ; k <= n * 2 ; k ++ )
    				f[d][j][k] = f[d ^ 1][j][k] * (1 - p[i]);
    		for(j = 0 ; j < n ; j ++ )
    			for(k = 1 ; k <= n * 2 ; k ++ )
    				f[d][j + 1][min(k + a , n * 2)] += f[d ^ 1][j][k] * p[i];
    	}
    	for(i = t ; i <= n ; i ++ )
    		for(j = n ; j <= 2 * n ; j ++ )
    			ans += f[n & 1][i][j];
    	printf("%.6lf
    " , ans);
    	return 0;
    }
    

     

  • 相关阅读:
    数组对象遍历新增属性
    watch监听数据的改变
    同一个数组查重
    SpringCloud搭建(二) 支付模块搭建
    SpringCloud搭建(一) 聚合父工程搭建
    线程池
    同步容器
    容器
    JVM学习
    线程---ThreadLocal
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7600007.html
Copyright © 2020-2023  润新知