codeforces 54C First Digit Law (数位dp+概率dp+背包模型)

题意:

给出n个区间,然后每个区间中取任何数的概率都是1/(r-l+1),现在问n个区间任意的组成的任意集合,求某个集合满足里面的数首位是1的个数大于k的概率。

题解:

说起来很绕,读了一个小时也没读懂啊!不看题解的翻译根本读不懂题目啊!我严重怀疑出题人的英语水平。

不过是一道好题,首先要得到区间中首位为1的数字个数要数位dp或者组合数学,然后就是背包模型的概率dp,因为要求任意字集合满足条件的格律,那么背包再合适不过,以去的区间个数作为重量,概率作为价值。之间用乘号。

#include<iostream>
#include<math.h>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<vector>
#include<map>
using namespace std;
typedef long long lld;
const int oo=0x3f3f3f3f;
const lld OO=1LL<<61;
const int MOD=(1e9)+7;
const int maxn=1005;
double dp[maxn],p[maxn];

lld Cnt(lld n)
{
    lld ans=0,x=1,cnt=0,high=0,num=n;
    while(num)
    {
        high=num%10;
        num/=10;
        cnt++;
    }
    for(int i=1;i<cnt;i++,x*=10)
        ans+=x;
    if(high>1)
        ans+=x;
    else if(high==1)
        ans+=n-x+1;
    return ans;
}

int main()
{
    int n,k;
    lld l,r;
    while(scanf("%d",&n)!=EOF)
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%I64d %I64d",&l,&r);
            lld temp=Cnt(r)-Cnt(l-1);
            p[i]=1.0*temp/(r-l+1);
        }
        scanf("%d",&k);
        memset(dp,0,sizeof dp);
        dp[0]=1.0;
        for(int i=1;i<=n;i++)
        {
            for(int j=n;j>=0;j--)
            {
                dp[j]=dp[j]*(1.0-p[i]);
                if(j>0)
                dp[j]+=dp[j-1]*p[i];
            }
        }
        double ans=0.0;
        for(int i=0;i<=n;i++)
            if(i*100>=n*k)
                ans+=dp[i];
        printf("%.15lf\n",ans);
    }

    return 0;
}
/**
1
1 2
50
2
1 2
9 11
50

*/







你可能感兴趣的:(codeforces 54C First Digit Law (数位dp+概率dp+背包模型))