试题 算法训练 印章

试题 算法训练 印章
动态规划:

资源限制

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

问题描述

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

输入格式

一行两个正整数n和m

输出格式

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

样例输入

2 3

样例输出

0.7500

数据规模和约定

1≤n,m≤20

---------------------------------------------------------------------------------------------------------------------------------

知识联想

高中数学的游戏

走到第五十步就得奖,求得奖概率。

 设p(n)为走到第n步概率,

容易得知p(1)=1(起点);

p(2)=0.5;(出发迈一步);

p(3)=0.5*p(2)+0.5*p(1);(出发迈两步和连续迈两步);

。。。。。。。。。。。

因此第n步就可分为两种情况,

一:

他是从第n-1步走一步过来的。

二:

他是从第n-2步走两步过来的。

那么两种情况都考虑到有

p(n)=0.5*p(n-1)+0.5*p(n-2);

---------------------------------------------------------------------------------------------------------------------------------

动态规划:

1.设置状态(看是一维数组还是二维数组,一般的题目都是二维数组,经验之谈)

2.找状态之间的关系,

3.写代码

这其中1,2是最难的,首先把状态想出来这一步就可以劝退很多人了,怎么想状态呢,把我平时做dp的思路跟你们说一下:

首先看题目,有买的印章数和印章种数两个变量,就自然而然地想成二维数组dp[i] [j],数组有了。状态呢?题目要求的是概率嘛,那我们dp数组的值就是概率。那么,i,j怎么说呢?题目说了小A买了m张,要凑齐n种,那么我们就设:dp[i] [j]表示买 i 张凑齐 j 种印章的概率。

状态设想出来,二维数组的表格先画出来(如上图)。然后寻找初始状态(一般都是i == 1,2,3或者j == 1,2,3这种i,j的值很小的)。

题目中有 n 种印章, 每种概率是 1/n;

先来看i<j的时候:因为买 i 张最多只能凑齐 i 种,所以这种情况概率为0

再来看j=1的时候:

i=1:dp[i] [1]表示的意思是买 i 张凑齐 1 种的概率,很明显 i == 1 && j == 1 的时候dp[1] [1] = 1,因为你买一张是无论如何都可以凑齐一种的,是吧。

i > 1:i > 1 && j == 1. 意思就是买 i 张凑齐 1 种印章的概率,意味着买的这 i 张印章是同一种,概率就是 (1/n)^i,但是,我们这 i 张要么是第一种,要么是第二种…( 要么是第k种 ,1 <= k <= n )。最后还要乘上n,所以这个情况 dp[i] [j] = (1/n)^(i-1)

初始状态大致就上面这些了:↑

最后来看中间的状态 dp[i] [j]:

我们买的第 i 张,有两种状态,一:跟前面买的 i-1 张中有重复的 二:跟前面买的 i-1 张中没有重复的

什么意思呢? 比如:(总共有5种印章,标号1,2,3,4,5) 我正在买第5张,前面四张凑齐了1,2,3,4号,那么第5张如果也是1,2,3,4号中的一个就表示重复的;如果第5张是5号,就表示没有重复的。

一:有重复的:就表明前面买的 i-1 张,已经凑齐了 j 种。dp[i] [j] = dp[i-1] [j] * ( j / n ); j/n是什么意思?

二:无重复的

前面买的 i-1 张 凑齐了 j-1 种, 我们买的第 i 张与前面的不重复,就又凑齐了一种:

dp[i] [j] = dp[i-1] [j-1] * (n-j+1) / n;

然后把两种情况加起来就是dp[i] [j] 的概率了!!

#include <iostream>
#include <cmath>
using namespace std;
double dp[25][25], p;
int main()
{
    
    //记住是小数啊,要*1.0进行类型转换的
    int n, m;
    cin >> n >> m;
    p = 1.0 / n; //每种出现的概率
    
    for ( int i = 1; i <= m; ++i ) {
        for ( int j = 1; j <= n; ++j ) {
            if ( i <  j ) dp[i][j] = 0;
            if ( j == 1 ) {
                dp[i][j] = pow (p, i-1);  //p^(i-1)
            }
            else {
                dp[i][j] = dp[i-1][j] * (j*1.0/n) + dp[i-1][j-1] * ((n-j+1)*1.0/n);
            }
        }
    }

    printf("%.4lf  ",dp[m][n]);
    return 0;
}


 

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

litian355

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

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

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

打赏作者

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

抵扣说明:

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

余额充值