概率dp

7 篇文章 0 订阅

来写一个概率dp专题


light oj 1027

有n个门让你选,每个门有一个数字,正数代表x分钟后出去,负数代表x分钟后回到起点重新开始

然后问你出去的时间期望是多少分钟

如果是一个正数,那么x分钟后就可以出去,如果是一个负数,那么等x分钟后,继续回到起点,那么答案就是

ans = (正数的个数 / 总个数) * (正数的和 / 正数的个数) + (负数的个数 / 总个数) * (负数的和 / 负数的个数 + ans)

化简后 ans = 总和(abs(x)) / (总个数 - 负数的个数)

int main(){
    int t;
    cin>>t;
    for(int ii=1;ii<=t;ii++){
        printf("Case %d: ",ii);
        int n,x,sum=0,num=0;
        cin>>n;
        for(int i=0;i<n;i++){
            cin>>x;
            if(x<0){
                num++;
            }
            sum+=abs(x);
        }
        if(num==n) printf("inf\n");
        else {
            int a=sum,b=n-num;
            printf("%d/%d\n",a/__gcd(a,b),b/__gcd(a,b));
        }
    }
    return 0;
}

light oj 1030

飞行棋,然后每个位置上有一个数字,6面骰子,问你从1走到n,走过的数的和的期望

概率dp求期望,倒着dp,dp[i]表示从i走到n的期望,因为你从i走到i+1,i+2,。。。i+6,i有1/6的概率转移到比如i+1,然后得到dp[i+1],所以dp[i]+=1/6(dp[i+1]+dp[i+2]+...+dp[i+6])。如果不满6格不一定是1/6

double dp[110];
 
int main(){
    int t;
    cin>>t;
    for(int ii=1;ii<=t;ii++){
        printf("Case %d: ",ii);
        int n,x,sum=0,num=0;
        cin>>n;
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++){
            cin>>dp[i];
        }
        for(int i=n-1;i>=1;i--){
            if(n-i<6){
                for(int j=i+1;j<=n;j++){
                    dp[i]=dp[i]+dp[j]*1.0/(n-i);
                }
            }
            else {
                for(int j=i+1;j<=i+6;j++){
                    dp[i]=dp[i]+dp[j]*1.0/6;
                }
            }
        }
        printf("%.6f\n",dp[1]);
    }
    return 0;
}

lightoj 1038

给你一个数n,然后从他的因子中选择一个d,然后得到n/d,然后还是这样操作,问你得到1需要的次数的期望

首先就能想到,只跟因子的个数有关,然后就犯了一个小错

正确的是,dp[i] = 求和((1 / sum) * (1 + dp[j])) (sum是i所有的因子的个数,j是i的所有的因子)

化简后 dp[i] = ( 求和(dp[I/j]]) + i因子的个数 ) / (i因子的个数 - 1)

double dp[110000];
 
void init(){
    memset(dp,0,sizeof(dp));
    for(int i=2;i<=100002;i++){
        double sum=0;
        int temp=0;
        for(int j=1;j*j<=i;j++){
            if(i%j==0){
                sum+=dp[j];
                temp++;
                if(j*j!=i){
                    sum+=dp[i/j];
                    temp++;
                }
            }
        }
        dp[i]=(sum+temp)*1.0/(temp-1);
    }
}
 
int main(){
    int t,n;
    init();
    cin>>t;
    for(int ii=1;ii<=t;ii++){
        cin>>n;
        printf("Case %d: %.7f\n",ii,dp[n]);
    }
    return 0;
}

codeforces 148d

题意:袋子里有w白,b黑,龙和王妃轮流抓,谁先抓到白谁赢,王妃每次抓一只,龙每次抓完一只,跑出去一只,全部都是随机,没人抓到白龙赢,问王妃赢的概率

题解:dp[i][j]表示i白j黑的时候,王妃赢的概率

            显然dp[i][0] =1,  dp[0][i] = 0

double dp[1010][1010];

int main() {
    int a,b;
    cin>>a>>b;
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=a;i++)
        dp[i][0]=1;
    for(int i=1;i<=a;i++){
        for(int j=1;j<=b;j++){
            ///直接取到白的概率
            dp[i][j]=i*1.0/(i+j);
            ///王妃取到黑,龙取到黑,跑出去一只黑
            if(j>=3){
                dp[i][j]+=j*1.0/(i+j)*(j-1)*1.0/(i+j-1)*(j-2)*1.0/(i+j-2)*dp[i][j-3];
            }
            ///王妃取到黑,龙取到黑,跑出去一只白
            if(j>=2){
                dp[i][j]+=j*1.0/(i+j)*(j-1)*1.0/(i+j-1)*i*1.0/(i+j-2)*dp[i-1][j-2];
            }
        }
    }
    printf("%.10f\n",dp[a][b]);
    return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值