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