笔试强训 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);
    }
}
相关推荐
Luhui Dev2 天前
大角几何 3D 渲染 API 上线:让立体题图进入自动化生产流程
人工智能·数学·luhuidev
闻缺陷则喜何志丹3 天前
【计算几何】Clipper的偏移
数学·偏移·计算几何·裁剪·clipper
syagain_zsx3 天前
算法基础篇 · 04 前缀和(C++ 题解)
c++·算法·前缀和
Logic1013 天前
C语言/数据结构滑动窗口题解:替换k个字符后的最长连续相同字符子串(LeetCode 424)
c语言·数据结构·字符串·滑动窗口·时间复杂度·频率统计·算法题
彧azz4 天前
算法设计与分析:贪心与动态规划
数据结构·学习·算法·贪心算法·动态规划
HIT_Weston4 天前
7、【数学】【基础】复数是什么:从解方程到旋转
数学·基础
铸人5 天前
再论勾股定理成立的条件-28
数学·几何学·复数·物理
Logic1015 天前
C语言/数据结构动态规划题解:Kadane算法求最大子数组和——O(n)时间O(1)空间
c语言·数据结构·动态规划·贪心·时间复杂度·算法题·最大子数组和
HIT_Weston5 天前
5、【数学】【基础】归一化的几何意义:球面、超平面与信息去哪了
数学·基础
梦帮科技6 天前
AI 音乐产品的发布工程:验证门、数据发布、回滚与生产运维纪律
数据结构·数据库·架构·node.js·音视频·动态规划·推荐算法