Bootstrap

【acm刷题--概率】Uva10028 集小卡片换奖品

寒假也没啥事,不如刷刷题呗~
在这里插入图片描述
Uva10288

题意

麦片盒子里的优惠券编号从1到n,奖品(麦片)需要一套盒子,当然)。每个盒子一张优惠券,平均需要多少个盒子才能完成一套n张优惠券?

输入

一串正整数n(1<=n<=33),以EOF结束

输出

将平均要拿多少次才能集齐一套共n个的数学期望E以带分数的形式输出,若结果为整数,就只输出整数的部分。

思路

假设当前集了k种小卡片,那么获得新小卡片的概率为 (n-k)/n, 需要的步数期望为 n/(n-k)。 求和可得总的步数期望为:
E = n / n + n / ( n − 1 ) + … + n / 1 = n ∑ i = 1 n 1 i E = n/n + n/(n-1) + … + n/1 = n\sum_{i=1}^{n}\frac{1}{i} E=n/n+n/(n1)++n/1=ni=1ni1
主体框架:

  • 三个数组保存1~33对应的整数部分、分子部分、分母部分。
  • 开始的时候先算出这99个数。
  • 一个while循环读入n,输出每次结果。

注意事项

  1. 及时约分
  2. 使用long long, int不够表示

样例输入

2

5

17

样例输出

3

5
11 –
12

340463
58 ------
720720

AC代码

#include<bits/stdc++.h>
using namespace std;
#define LL long long
LL lef[35],top[35],bottom[35];
LL n;
//	E = n * (1/n + 1/(n-1) + … + 1/1)
inline LL _gcd_(LL fst, LL scd){
    return (fst==0)?scd:_gcd_(scd%fst, fst);
}
void callculates() {
    memset(lef, 0, sizeof(lef));
    memset(top, 0, sizeof(top));
    memset(bottom, 1, sizeof(bottom));
    lef[1] = 1;top[1] = 0;bottom[1] = 1;
    for (int i=2; i<34; i++) {		//(1/n + 1/(n-1) + … + 1/1)
        LL up = 1, down = 1,gcd;
        for(int j=2; j<=i; j++) {	//+1/j
            up = up*j + down;
            down = down * j;
            gcd = _gcd_(up, down);
            up /= gcd;
            down /= gcd;
        }
        up *= i;
        gcd = _gcd_(up, down);
        up /= gcd;
        down /= gcd;
        lef[i] = up/down;
        top[i] = up%down;
        bottom[i] = down;
    }
}
int main(){
    //freopen("input.txt", "r", stdin);
    callculates();
    while(~scanf("%d", &n)) {
        if (top[n] == 0) {printf("%d\n", lef[n]);}
        else {
            for(LL i = lef[n]; i>0; i/=10)printf(" ");printf(" %lld\n", top[n]);
            printf("%lld ",lef[n]);
            for(LL i = bottom[n]; i>0; i/=10)
                printf("-");
            printf("\n");
            for(int i = lef[n]; i>0; i/=10)printf(" ");printf(" %lld\n", bottom[n]);
        }
    }
    return 0;
}
;