(新版)SJTU-OJ-1034. 小源吃苹果

题目描述

小源有 n n n 个苹果,他每天都会吃掉一些苹果。

但是小源是个没有主见的人,他每天到底要吃多少个苹果取决于当天剩下多少个。当这一天剩下 k k k 个苹果时,他等概率(即 1 k \frac{1}{k} k1 吃掉 1 , 2 , ⋯   , k 1,2,\cdots,k 1,2,,k 个苹果。

现在想要知道,小源吃掉 m m m 个苹果期望下需要多少天。

输入格式

一行两个整数 n , m n,m n,m,表示最初苹果数和目标苹果数。

输出格式

一个数表示期望天数,保留到小数点后两位。

样例输入

3 3

样例输出

1.83

数据范围

对于 30 % 30\% 30% 的数据有

m ≤ 3 m\leq3 m3

对于 50 % 50\% 50% 的数据有

n ≤ 10 n\leq10 n10

对于 20 % 20\% 20% 的数据有

n = m n=m n=m

对于 100 % 100\% 100% 的数据有

1 ≤ m ≤ n ≤ 20 1\leq m\leq n\leq20 1mn20

函数递归

      本题用到的是函数递归,关于递归的思考:

      递归函数就是自己给自己挖一个洞,我调用我自己。【例如,下面的大圆圈表示第一次调用函数的函数入口,函数运行后又调用了自己,相当于大圆圈里面还有一个小圆圈,同理小圆圈继续调用自身这个函数,就形成了一个无底洞】。
       显然这是不科学的!为了避免无底洞现象,必须设置终止的条件!!!
请添加图片描述
       所以,写递归函数按照下面的语法来!可以避免无底洞现象。

void function(参数)
{
	// 写清楚终止条件
	if(终止条件)
		return;
	else
	// 根据实际情况情况调用自身这个函数
		function(参数);
}

       递归函数,实际是一个过程,比如说,模拟一个过程,比如一次走步,一次决定,或者其他……这个过程要一直执行下去,直到某一个过程。
       我们举个例子,求下面方程的解:
x 1 + x 2 + x 3 + x 4 + x 5 = 10 ( x 1 , x 2 , x 3 , x 4 , x 5 ∈ N ) x_1+x_2+x_3 + x_4 +x_5=10(x_1,x_2,x_3,x_4,x_5 \in N) x1+x2+x3+x4+x5=10x1,x2,x3,x4,x5N
       就是可以利用一个递归函数来解决的。我们先求 x 1 x_1 x1 的可能值,然后求 x 2 x_2 x2 的可能值,一直求到 x 5 x_5 x5 的值,递归停止,输出结果。
       另外,要注意两个函数之间的通讯值,首先:轮数,其次,排除前面几轮数字的和剩下的数值!

#include <iostream>
using namespace std;

int a[6];
// i 反映当前的轮数,每轮函数查找 x_i 的可能结果,remain反映的remain
void findAns(int i,int remain)
{
    if (i == 5)
    {
        a[5] = remain;
        for (int q = 1; q <= 5; q++)
        {
            cout << a[q] << "\t";
        }
        cout << endl;
    }
    else
        for (int q = 0; q <= remain; q++)
        {
            a[i] = q;
            findAns(i + 1, remain - q);
        }
}

int main()
{
    findAns(1, 10);
    system("pause");
    return 0;
}

题目解答

       所以,同样的道理,写递归函数首先要明白一轮函数执行下来模拟的过程是什么?这道题递归函数一轮下来模拟的就是一天下来吃的苹果。
       首先我定义了一个类,然后开一个关于这个类的 很大的数组,数组里面每一个元素是一种解决方案,每个元素包括两个要点 1-吃完要的天数 2-可能性。
       按照概率统计的知识,这个可能性反映的是全局可能性,而我们求期望需要用的是条件概率。

方案编号12345678 ⋯ \cdots
吃完用的天数
全局可能性
归一化 概率

       其中,方案是指所以满足题目要求的方案,即为 小源吃掉 m m m 个苹果的解决方法
       其中,全局可能性是:基于该方案,每天吃相应苹果数的概率的乘积。

        问题: 为什么要设置归一化概率?
        解答: 众所周知,计算期望用的概率,所有概率的和是1,那么反问一下自己,全局可能性求和一定是1吗?当且仅当 m = n m=n m=n 满足,其余不是,所以概率要归一化,就是把所有解决方案的全局可能性求和,用 每一个全局可能性 除以 全局可能性求和,得到对应的归一化概率,然后用归一化概率求期望。

        问题: 数组要开多大?
        解答: 尽可能大,要包括所有的解决方案。测试结果是需要一百万。(少一个数量级就不能通过!)

        问题: 怎么设置两位小数点输出?
        解答: 方法:cout << fixed << setprecision(2) << result;
        补充: 没用过的可以看看下面的总结

  • 头文件要加: #include <iomanip>
  • cout << fixed << setprecision(2) << result;是设置小数点后2位的
  • cout << setprecision(2) << result;是设置有效位数2位的
  • C++的流输出 cout 默认是 cout << setprecision(6) ; 有效数字6位。
  • 注意,一旦使用了这个 setprecision ,后续的所有输出都会生效!除非手动代码设置恢复默认
            问题: 有AC代码吗?
            解答: 如下:
#include <iostream>
#include <cmath>
#include <iomanip>
using namespace std;
int m, n;
double allpossible = 0;
double result = 0;

class possible_result
{
	public:
    	int day;
    	double possiblility;
};

possible_result temp[1000000];  // 气死我了要开一百万呜呜呜
int cnt = 0;

void eat(int remain,int day,double p)
{
    if (remain == n - m)
    {
        // cout << day - 1 << "|" << p << endl;
        temp[cnt].day = day - 1;
        temp[cnt].possiblility = p;
        allpossible = allpossible + p;
        cnt++;
        return;
    }

    for (int i = 1; i <= remain && i <= m; i++)
    {
        eat(remain - i, day + 1, p / remain);
    }
}

int main()
{
    cin >> n >> m;
    eat(n, 1, 1);

    // getresult(n, 1, 1);
    for (int j = 0; j < cnt; j++)
    {
        result = result + temp[j].day * temp[j].possiblility / allpossible;
    }
    cout << fixed << setprecision(2) << result;

    system("pause");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值