HDU 4978 A simple probability problem. 蒲丰投针问题 + 二维凸包

题目大意:

就是现在有一个平面上铺满了间距为D的平行直线, 现在有一个圆的直径是D, 将这个圆的中心放在原点处之后给出N( N <= 100)个点的坐标, N个点都在圆上或者圆内, 现在这N个点两两之间都有线段相连, 问如果将这个圆随机地放置到铺满间距为D的平面上, 这N个点之间的连线与平面上的平行线相交的概率, 结果保留小数点后四位


大致思路:

当时看的时候觉得是一个神题, 只会积分手算第一个样例, 后来发现这是一个概率上的结论题

首先需要知道这样一个概率模型:

蒲丰投针问题(Buffon needle problem)

 18世纪, 法国数学家布丰和勒克莱尔提出的“投针问题”, 记载于布丰1777年出版的著作中:“在平面上画有一组间距为D的平行线, 将一根长度为L(L < D)的针任意投掷在这个平面上, 求此针与平行线中任一直线相交的概率. 布丰本人证明了这个概率是:

 P = 2*L/(π*D)

另外参考有关论文可以发现对于蒲丰投针问题的扩展结论

当投掷物是一般的平面凸曲线时的蒲丰问题 平面内任何一个凸曲线,都可以有一列凸多边形来逼近(当凸多边形的边数趋于无穷大时),在这列凸多边形中取极限的过程,就可得到凸曲线。例如,圆可以由正n边形来逼近(n→∞)。因此,可以不加证明地指出:平面凸曲线的蒲丰问题与凸多边形的蒲丰问题有相同的结果,也就是说,平面上画有等距离D(D>0)的平行线,向平面上任意投掷一个直径为d(d<D)的二维凸曲线,设凸曲线周长为C,则凸曲线与平行线相交的概率为P=C/(π*D)


那么对于本题的思路就很明显了, 如果存在两点的连线与平面上的直线相交, 则这N个点的凸包必定与平面上的直线相交, 如果凸包与直线相交, 必然存在两点连线与平面上的线相交(凸包的边就是N个点中一些点的连线)

所以只需要求出这N个点的凸包的周长然后结合蒲丰投针问题的扩展结论即可求得概率

代码如下:

Result  :  Accepted     Memory  :  1120 KB     Time  :  15 ms

/*
 * Author: Gatevin
 * Created Time:  2014/12/27 15:52:34
 * File Name: Sora_Kasugano.cpp
 */
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;

const double PI = acos(-1.0);

int sgn(double d)
{
    return d < -eps ? -1 : (d > eps ? 1 : 0);;
}

struct point
{
    double x, y;
    point(double _x = 0, double _y = 0) : x(_x), y(_y) {}
    void input()
    {
        scanf("%lf %lf", &x, &y);
        return;
    }
    double len() const
    {
        return sqrt(x*x + y*y);
    }
    void output() const
    {
        printf("%.3f %.3f\n", x, y);
    }
};

point operator + (const point &p1, const point &p2)
{
    return point(p1.x + p2.x, p1.y + p2.y);
}

point operator - (const point &p1, const point &p2)
{
    return point(p1.x - p2.x, p1.y - p2.y);
}

double operator ^ (const point &p1, const point &p2)
{
    return p1.x*p2.x + p1.y*p2.y;
}

double operator * (const point &p1, const point &p2)
{
    return p1.x*p2.y - p2.x*p1.y;
}

bool operator < (point p1, point p2)
{
    return sgn(p1.x - p2.x) == 0 ? sgn(p1.y - p2.y) < 0 : p1.x < p2.x;
}

point p[110];
point pol[110];

int dn, hd[110], un, hu[110];
void getConvexHull(point *p, int n, point *pol, int &m)//求二维凸包
{
    sort(p, p + n);
    dn = un = 2;
    hd[0] = hu[0] = 0;
    hd[1] = hu[1] = 1;
    for(int i = 2; i < n; i++)
    {
        for(; dn > 1 && sgn((p[hd[dn - 1]] - p[hd[dn - 2]]) * (p[i] - p[hd[dn - 1]])) <= 0; dn--);
        for(; un > 1 && sgn((p[hu[un - 1]] - p[hu[un - 2]]) * (p[i] - p[hu[un - 1]])) >= 0; un--);
        hd[dn++] = hu[un++] = i;
    }
    m = 0;
    for(int i = 0; i < dn - 1; i++)
        pol[m++] = p[hd[i]];
    for(int i = un - 1; i > 0; i--)
        pol[m++] = p[hu[i]];
}


int main()
{
    int t;
    scanf("%d", &t);
    int N, D;
    int cnt;
    for(int cas = 1; cas <= t; cas++)
    {
        scanf("%d %d", &N, &D);
        for(int i = 0; i < N; i++) p[i].input();
        getConvexHull(p, N, pol, cnt);
        double C = 0;
        for(int i = 1; i < cnt; i++)
            C += (pol[i] - pol[i - 1]).len();
        C += (pol[0] - pol[cnt - 1]).len();
        double ans = C/(PI*D);
        printf("Case #%d: %.4f\n", cas, ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值