笔试强训 Day 43:kotori 和抽卡(二)、ruby 和薯条、循环汉诺塔

Day 43

kotori 和抽卡(二)

解题思路:这不是"数学期望",而是"二项分布中的概率":n 次抽卡中,恰好有 m 次抽到 R 卡的概率。

思路可以分成三步。

第一步,计算 C(n, m)

C(n, m) 表示:在 n 次抽卡中,选择哪 m 次抽到 R 卡。

公式是:

C(n,m)=\\frac{n!}{m!(n-m)!}

C(5, 3) = (5 * 4 * 3) / (3 * 2 * 1)

代码中:

复制代码
for (int i = n; i >= n - m + 1; i--) {
    ret *= i;
}

计算的是:

n \\times (n-1) \\times \\cdots \\times (n-m+1)

一共有 m 个数。

然后:

复制代码
for (int i = m; i >= 2; i--) {
    ret /= i;
}

除以:

m!

由于前面没有乘上完整的 n!,所以实际上已经隐含完成了:

\\frac{n!}{(n-m)!m!}=C(n,m)

第二步,计算 m 次抽到 R 卡的概率:

复制代码
for (int i = 0; i < m; i++) {
    ret *= 0.8;
}

也就是:

0.8\^m

第三步,计算剩下 n-m 次没有抽到 R 卡的概率:

复制代码
for (int i = 0; i < n - m; i++) {
    ret *= 0.2;
}

R 卡概率是 0.8,所以非 R 卡概率是:

1-0.8=0.2

因此是:

0.2\^{n-m}

最终公式就是:

\\boxed{C(n,m)\\times 0.8\^m\\times 0.2\^{n-m}}

例如输入:

复制代码
3 2

表示抽 3 次,恰好抽到 2 张 R 卡。

可能的情况有:

复制代码
R R 非R
R 非R R
非R R R

一共有:

C(3,2)=3

每种情况的概率是:

0.8\^2\\times0.2

所以总概率:

3\\times0.8\^2\\times0.2=0.384

输出:

复制代码
0.3840

代码实现:

java 复制代码
import java.util.*;

public class Main{
    
    public static void main(String[] args){
        Scanner in = new Scanner(System.in);
        int n = in.nextInt(), m = in.nextInt();
        double ans = 1.0;
        
        // 计算 C(n, m)
        for(int i = n; i > n - m; i--) ans *= i;
        for(int i = m; i > 0; i--) ans /= i;
        
        // m 次抽到 R 卡
        for(int i = 0; i < m; i++) ans *= 0.8;
        
        // n - m 次没有抽到 R 卡
        for(int i = 0; i < n-m; i++) ans *= 0.2;
        System.out.printf("%.4f", ans);
    }
}

ruby 和薯条

解法一:排序 + 二分查找

这两个二分其实是在找同一个合法区间的左右边界。

对于当前的 nums[i],我们要找前面的 nums[j],满足:

复制代码
l <= nums[i] - nums[j] <= r

移项得到:

复制代码
nums[i] - r <= nums[j] <= nums[i] - l

代码实现:

java 复制代码
import java.util.*;

public class Main {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);

        int n = in.nextInt();
        long l = in.nextLong();
        long r = in.nextLong();

        long[] nums = new long[n];

        for (int i = 0; i < n; i++) {
            nums[i] = in.nextLong();
        }

        Arrays.sort(nums);

        long cnt = 0;

        for (int i = 1; i < n; i++) {

            /*
             * l <= nums[i] - nums[j] <= r
             *
             * 等价于:
             * nums[i] - r <= nums[j] <= nums[i] - l
             */

            long high = nums[i] - l;

            // 找第一个大于 high 的位置
            int left = 0;
            int right = i;

            while (left < right) {
                int mid = left + (right - left) / 2;

                if (nums[mid] <= high) {
                    left = mid + 1;
                } else {
                    right = mid;
                }
            }

            int rightBound = left;

            long low = nums[i] - r;

            // 找第一个大于等于 low 的位置
            left = 0;
            right = i;

            while (left < right) {
                int mid = left + (right - left) / 2;

                if (nums[mid] < low) {
                    left = mid + 1;
                } else {
                    right = mid;
                }
            }

            int leftBound = left;

            // 合法下标范围是 [leftBound, rightBound)
            cnt += rightBound - leftBound;
        }

        System.out.println(cnt);
    }
}

解法二:前缀和 + 滑动窗口

代码实现:

java 复制代码
import java.util.*;

public class Main {
    public static int n, l, r;
    public static int[] arr;

    // 找出差值在 [0, x] 区间内一共有多少对
    public static long find(int x) {
        int left = 0;
        int right = 0;
        long ret = 0;

        while (right < n) {
            while (arr[right] - arr[left] > x) {
                left++;
            }

            ret += right - left;
            right++;
        }

        return ret;
    }

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);

        n = in.nextInt();
        l = in.nextInt();
        r = in.nextInt();

        arr = new int[n];

        for (int i = 0; i < n; i++) {
            arr[i] = in.nextInt();
        }

        Arrays.sort(arr);

        System.out.println(find(r) - find(l - 1));
    }
}

循环汉诺塔

解题思路:

代码实现:

java 复制代码
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);

        int n = in.nextInt();

        int x = 1;
        int y = 2;
        int MOD = 1000000007;

        for (int i = 2; i <= n; i++) {
            int xx = x;
            int yy = y;

            x = (2 * yy + 1) % MOD;
            y = ((2 * yy + 2) % MOD + xx) % MOD;
        }

        System.out.println(x + " " + y);
    }
}
相关推荐
lvwangshu1 天前
P6534 [COCI 2015/2016 #1] UZASTOPNI 等差树列 题解
动态规划·图论·题解·性质题
lvwangshu1 天前
线性代数基础 Linear Algebra Basics
数学·算法
小玮看世界2 天前
[Python]动态规划三步走:从爬楼梯到打家劫舍,再到最大子数组和
开发语言·python·动态规划
旖旎夜光2 天前
LeetCode 974:和可被 K 整除的子数组(前缀和) —— 题解
数据结构·c++·算法·leetcode·前缀和
旖旎夜光4 天前
LeetCode 238:除自身以外数组的乘积(前缀和) —— 题解
数据结构·c++·算法·leetcode·前缀和
金銀銅鐵5 天前
斐波那契数列中连续10项的和总是能被11整除吗?
python·数学
2601_956121975 天前
线性DP(入门)
c++·算法·动态规划
lvts_cs6 天前
如何评估化工产业规划的质量
大数据·人工智能·动态规划
金銀銅鐵7 天前
斐波那契数列的个位数出现的周期是多少?
python·数学
lvts_cs7 天前
化工产业规划对城市发展的具体指导作用
算法·动态规划