笔试强训 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);
    }
}
相关推荐
旖旎夜光14 小时前
LeetCode 3:无重复字符的最长子串(滑动窗口) —— 题解
数据结构·c++·算法·leetcode·滑动窗口
hanlin0319 小时前
动态规划专练:力扣第718、1143题
算法·leetcode·动态规划
hanlin031 天前
动态规划专练:力扣第1035、392题
算法·leetcode·动态规划
Tasiorh2 天前
《基于LSTM-RNN-CBAM模型和动态规划模型的交易策略》
rnn·lstm·动态规划
Luhui Dev2 天前
如何在 WorkBuddy 中使用大角几何:从 MCP 接入到 AI 几何作图
人工智能·数学·算法·agent·luhuidev
幸福回头3 天前
Tyvj 1246 EZ_lzh☆数字格子
动态规划
cpp_25014 天前
P1113 [USACO02FEB] 杂务
数据结构·c++·算法·动态规划·图论·拓扑排序·洛谷题解
漂流瓶jz4 天前
UVA-12174 Shuffle的播放记录 题解答案代码 算法竞赛入门经典第二版
数据结构·c++·算法·图论·滑动窗口·算法竞赛入门经典·uva
VL——MOESR4 天前
【LuoguP6772】美食家【DP】【矩阵】【倍增】
c++·算法·矩阵·动态规划·倍增