首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> C++ 入門

2014 BNU 邀請賽 F 題 Football on Table:幾何概率與桿狀物體碰撞計算

     編輯:C++ 入門

題目大意

桌上足球遊戲:有 n 根桿,每根桿上有若干「人」(寬度已知),桿可在垂直方向滑動。球從 (x,y) 以速度 (dx,dy) 射出,求球不碰到任何人的機率。假設桿滑動均勻隨機。

核心思路:獨立事件機率相乘

各桿滑動獨立,球不碰到任何人 = 所有桿都不碰到人。總機率 = ∏ 各桿不碰到機率。

單桿不碰到機率計算

幾何轉換

  • 球軌跡:直線 y = y0 + (dy/dx) * (x - x0)。
  • 桿位置固定 x = x_rod,球通過時的 y 座標:y_cross = y + (dy/dx) * (x_rod - x)。
  • 桿可滑動範圍:[0, w - total_people_width](設桿寬為 w,人總寬 sum_r,間隙總和 sum_d,w = sum_r + sum_d)。

區間覆蓋模型

每個「人」在桿滑動過程中會掃過一個垂直區間。球在 y_cross 位置通過,若 y_cross 落入任一人掃過的區間 → 碰撞。

問題轉化為:在桿滑動範圍 [0, L](L = w - sum_r)中,隨機選取位移 offset,求 y_cross 不落入任何「人掃過區間」的機率。

掃過區間計算

  • 第 i 個人:寬度 r[i],與前一人間隙 d[i-1](首人前無間隙)。
  • 人 i 在桿上的相對位置區間:[pre_sum_r + pre_sum_d, pre_sum_r + pre_sum_d + r[i]]。
  • 滑動 offset ∈ [0, L] 後,該人覆蓋的絕對區間:[pre_sum_r + pre_sum_d + offset, pre_sum_r + pre_sum_d + r[i] + offset]。
  • 等價於:offset ∈ [y_cross - (pre_sum_r + pre_sum_d + r[i]), y_cross - (pre_sum_r + pre_sum_d)] 時發生碰撞。

碰撞總長度與機率

將所有人的碰撞 offset 區間在 [0, L] 上投影並合併,總長度為 collision_len。則:

P(不碰到) = 1 - collision_len / L

代碼邏輯對應

原代碼中 cal(gan g) 函式實現上述邏輯:

  • yy = g.len * dy / dx + y:球通過該桿的 y 座標。
  • yi = w - g.sumd:桿可滑動總長度 L。
  • 迴圈遍歷每個人,計算其碰撞 offset 區間與 [0, L] 的交集長度,累加到 ans。
  • 最終返回 1.0 - ans / yi。

完整程式碼(整理版)

#include <bits/stdc++.h>
using namespace std;
struct Rod {
    double len;       // 桿長(即 w)
    int num;          // 人數
    vector r; // 人寬度
    vector d; // 間隙
    double sumd;      // 總間隙 = 總人寬 + 總間隙 = w
    void read() {
        cin >> len >> num;
        r.resize(num); d.resize(num);
        sumd = 0;
        for (int i = 0; i < num; ++i) { cin >> r[i]; sumd += r[i]; }
        for (int i = 0; i < num-1; ++i) { cin >> d[i]; sumd += d[i]; }
        d[num-1] = 0;
    }
    double safeProb(double x, double y, double dx, double dy) const {
        double yy = len * dy / dx + y;      // 球通過此桿的 y
        double L = len - sumd;              // 可滑動範圍
        if (L <= 0) return 0;               // 無滑動空間,必碰
        double collision = 0;
        double pre = 0;                     // 當前人起始相對位置
        for (int i = 0; i < num; ++i) {
            double person_start = pre;
            double person_end = pre + r[i];
            // 碰撞 offset 區間:[yy - person_end, yy - person_start]
            double c_start = yy - person_end;
            double c_end = yy - person_start;
            // 與 [0, L] 取交集
            double inter_l = max(0.0, c_start);
            double inter_r = min(L, c_end);
            if (inter_r > inter_l) collision += inter_r - inter_l;
            pre = person_end + d[i];
        }
        return 1.0 - collision / L;
    }
};
int main() {
    ios::sync_with_stdio(false); cin.tie(nullptr);
    int T; cin >> T;
    for (int cas = 1; cas <= T; ++cas) {
        double l, w; cin >> l >> w;
        double x, y, dx, dy; cin >> x >> y >> dx >> dy;
        int n; cin >> n;
        double ans = 1.0;
        for (int i = 0; i < n; ++i) {
            Rod rod; rod.read();
            ans *= rod.safeProb(x, y, dx, dy);
        }
        cout << "Case #" << cas << ": " << fixed << setprecision(5) << ans << '\n';
    }
    return 0;
}

關鍵細節

  • dx 可能為 0?題目保證球最終會射向球門方向,dx ≠ 0。
  • 桿長 len 即為 w(寬度),滑動方向垂直於球軌跡投影。
  • 人與間隙輸入順序:先所有人寬度 r[],再間隙 d[](共 num-1 個)。
  • 浮點精度:用 double,輸出 %.5f。

總結

本題將幾何碰撞轉化為一維區間覆蓋機率,利用桿滑動獨立性將總機率分解為單桿機率乘積。核心難點在於正確建立「人掃過區間」與「球通過位置」的幾何對應關係,並處理區間交集計算。代碼邏輯清晰,是幾何概率題的優秀範例。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved