【概率DP】AHU-600 数字游戏

69 篇文章 0 订阅
数字游戏
Time Limit: 1000 ms   Case Time Limit: 1000 ms   Memory Limit: 64 MB
Description
cxlove对数字情有独钟,最近又开始玩一个有趣的数字游戏。
首先我们定义一种Lucky number:最高位为1的数字(10进制)。
接下来,会给你n个区间,[Li,Ri]
随机从每一个区间内取出1个整数。
问取出的这n个数中至少有K%是Lucky number的概率是多少。

Input
一个整数 T,表示T组数据。(1<=T<=50)
一个整数n,表示区间的个数 (1<=n<=1000)
接下来n行,每一行两个整数Li,Ri表示区间[Li,Ri],并且保证(1<=Li<=Ri<=10^18)
最后一行是一个整数k (0<=k<=100)

Output
一个实数,表示至少有k%是Lucky Number的概率,小数点后保留6位。

Sample Input
Original Transformed
2
1
1 2
50
2
1 2
9 11
50

Sample Output
Original Transformed
0.500000
0.833333

————————————————————————————————————————————————————————

题意:给出n个区间。设'1'开头的数字为幸运数字。从n个区间中取出n个数字。问取出的数字至少有k%是幸运数字的概率。

思路:本来只是一个简单DP,我却调了N久。

警示1:整数的n次方,再也不要用自带的pow()函数。自己写!心情好还能写个快速幂。因为自带的pow()函数是double类型的,精度损失非常大

警示2:区间右端点减去区间左端点,左端点要先减去1。防止左端点被减掉。

警示3:一定要根据dp数组的状态描述仔细地进行初始化。

设dp[i][j]表示前i个区间选到了j个幸运数字的概率。

那么dp[0][0] = 1(0个区间一定是0个幸运数字)

设每个区间选择到幸运数字的概率是v[i]

状态转移方程为:

dp[i][j] = dp[i-1][j-1] * v[] + dp[i-1][j] * (1-v[])

要么是第i个区间选择到了幸运数字,要么是前i-1个区间已经选够了j个幸运数字。

由此我们需要初始化dp[i][0]。因为1个幸运数字都没有选到,所以一路乘即可。

得到至少k%个幸运数字其实就是n * k%想上取整而已。设至少p个。之后把dp[][p] + dp[][p+1] + ... + dp[][n]即可。

现在剩下的问题是预处理区间内幸运数字的个数。这样就可以尽快查询到。(10^18很大啊)。

0~9:1个

10~99:10个

100~999:100个

......

取出左右端点将它们“剪裁”成上述片段即可。这里有用到前缀和。

代码如下:

/**
 * ID: j.sure.1
 * PROG:
 * LANG: C++
 */
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <ctime>
#include <cmath>
#include <stack>
#include <queue>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <climits>
#include <iostream>
#define For(i, x, y) for(int i=x; i<y; i++)
#define For_(i, x, y) for(int i=x; i>=y; i--)
#define Mem(f, x) memset(f, x, sizeof(f))
#define Sca(x) scanf("%d", &x)
#define Pri(x) printf("%d\n", x)
#define LL long long
using namespace std;
const int INF = 0x3f3f3f3f;
/****************************************/
const int N = 1111;
LL table[20];
double val[N], dp[N][N];
int n, k;

LL power(int i, int k)
{
    LL j = 1;
    while(k--) {
        j *= i;
    }
    return j;
}

void get_table()
{
    for(LL i=0,k=10; i<18; i++) {
        table[i] = power(k, i);
        table[i] += i ? table[i-1] : 0;
    }
}

LL get_num(LL x)
{
    char s[25];
    sprintf(s, "%lld", x);
    int len = strlen(s);
    if(len == 1) {
        return x ? 1 : 0;
    }
    if(s[0] == '1') {
        return x - power(10, len-1) + 1 + table[len-2];
    }
    else {
        return table[len-1];
    }
}

int main()
{
#ifdef J_Sure
    freopen("000.in", "r", stdin);
    freopen("999.out", "w", stdout);
#endif
    int T;
    get_table();
    Sca(T);
    while(T--) {
        Sca(n);
        LL x, y;
        For(i, 0, n) {
            scanf("%lld%lld", &x, &y);
            //the number of Lucky Number in [x, y]
            val[i] = get_num(y) - get_num(x-1);
            //the probability being Lucky Number
            val[i] /= y - x + 1;
        }
        Sca(k);
        Mem(dp, 0);
        dp[0][0] = 1;
        For(i, 1, n+1) {
            dp[i][0] = dp[i-1][0] * (1-val[i-1]);
        }
        //j Lucky Number have been selected of the first i intervals
        //dp[i][j] = dp[i-1][j-1] * val[i] + dp[i-1][j] * (1-val[i]);
        For(i, 1, n+1) {
            For(j, 1, i+1) {
                dp[i][j] += dp[i-1][j-1] * val[i-1] + dp[i-1][j] * (1-val[i-1]);
            }
        }
        double ans = 0;
        k = ceil(1.0*k/100.0*n);
        For(j, k, n+1) {
            ans += dp[n][j];
        }
        printf("%.6f\n", ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值