蓝桥杯-算法训练 印章

试题 算法训练 印章

资源限制

时间限制:1.0s 内存限制:256.0MB

问题描述

共有n种图案的印章,每种图案的出现概率相同。小A买了m张印章,求小A集齐n种印章的概率。

输入格式

一行两个正整数n和m

输出格式

一个实数P表示答案,保留4位小数。

样例输入

2 3

样例输出

0.7500

数据规模和约定

1≤n,m≤20

题解

思路

刚刚看到题目的时候,一开始以为是用高中学的概率论,组合数来做,但是后来发现自己实在是想不到,太复杂了,而且概率论也很久没学了,那么没办法了,只能是用动态规划来解决了

动态规划主要分为两步

第一步:确定初始状态

首先,我们知道,如果挑出一张牌,想要只抽中一种牌的概率肯定为1,接着,我们往下继续分析

我们现在思考一下,如果挑出i张牌,最后只抽中1种牌的概率是多少?

第一次肯定是1,第二次还是得抽中这张牌,那么概率就是 1 n \frac{1}{n} n1,第三次还是这张牌,概率为 ( 1 n ) 2 (\frac{1}{n})^2 (n1)2,推算下来,我们想要i张牌全部抽中1种牌的概率就是 ( 1 n ) i − 1 (\frac{1}{n})^{i-1} (n1)i1

至此,初始状态已经确定了

特殊的,当 i < j i<j i<j时,想要从使 i i i张牌中有 j j j种牌,一定是不存在的,则概率为0

第二步:推算动态转移方程

这题动态转移方程有点难推理,但是也不是不能推理

当前我们要抽i张牌,并且要保证这i张牌中正好有j种牌,现在我们要对抽 i − 1 i-1 i1张牌的状态进行分析

  1. 假设我们在抽第 i − 1 i-1 i1张牌的时候,已经有j种牌了,那么此时,我们在抽第 i i i张牌时还要保证这i张牌正好有j种牌,那么我们这时候不得不抽中这j种牌中任何一张,所以概率是 d p [ i − 1 ] [ j ] ∗ j n dp[i-1][j]*\frac{j}{n} dp[i1][j]nj
  2. 假设我们在抽第 i − 1 i-1 i1张牌是,手上只有 j − 1 j-1 j1种牌,那么此时,我们要保证这 i i i张牌中正好有j种牌,我们只能从这 j − 1 j-1 j1种牌外再抽出一张,概率为 d p [ i − 1 ] [ j − 1 ] ∗ n − j + 1 n dp[i-1][j-1] * \frac{n-j+1}{n} dp[i1][j1]nnj+1

最后我们可以得出动态转移方程:
d p [ i ] [ j ] = d p [ i − 1 ] [ j ] ∗ j n + d p [ i − 1 ] [ j − 1 ] ∗ n − j + 1 n dp[i][j] = dp[i-1][j]*\frac{j}{n}+dp[i-1][j-1]*\frac{n-j+1}{n} dp[i][j]=dp[i1][j]nj+dp[i1][j1]nnj+1
至此,此题已经分析完毕,剩下按照基本方法推就行了

java代码

import java.util.Scanner;

/**
 * @author 王宇哲
 */
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        int n = scanner.nextInt();
        int m = scanner.nextInt();

        //第一维界表示的是买彩票的张数,第二维界表示要凑齐的张数
        double[][] dp = new double[m+1][n+1];

        //买一张只凑齐1种的概率一定为1
        dp[1][1] = 1;

        //初始化
        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){

                if(i<j){
                    dp[i][j] = 0;
                }

                //买i张(只)凑齐1张的概率
                if(j == 1 && i != 1){
                    dp[i][j] = Math.pow(1.0/n,i-1);
                }
            }
        }

        for(int i=1;i<=m;i++){
            for(int j=1;j<=n;j++){
                //防止重复赋值
                if(!(j == 1 && i != 1) && !(i==1 && j==1)){

                    //前面j种已经凑齐了,又抽到了前面j中
                    dp[i][j] += dp[i-1][j]*((double) j/n);
                    //前面i张已经凑齐了j-1中,这次抽到其他的牌的概率
                    dp[i][j] += dp[i-1][j-1] * ((n-j+1.0)/n);
                }
            }
        }

        System.out.printf("%.4f",dp[m][n]);

    }
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一个老蒟蒻

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值