【SCOI2015】小凸想跑步

博客介绍了SCOI2015比赛中的一道题目,涉及概率计算和几何问题。小凸在操场跑步时玩了一个游戏,需要找到随机站位使得形成最小面积三角形的概率,并提供了输入输出格式、样例及解题思路。解题方法涉及到数学、不等式组和半平面交的概念,适合于算法和竞赛编程爱好者阅读。

内存限制:256 MiB 时间限制:1000 ms

问题描述

小凸晚上喜欢到操场跑步,今天他跑完两圈之后,他玩起了这样一个游戏。
操场是个凸 n 边形,N 个顶点按照逆时针从 0∼n−1 编号。现在小凸随机站在操场中的某个位置,标记为 P 点。将 P 点与 n个顶点各连一条边,形成 N 个三角形。如果这时 P 点, 0 号点,1 号点形成的三角形的面积是 N个三角形中最小的一个,小凸则认为这是一次正确站位。
现在小凸想知道他一次站位正确的概率是多少。

输入格式

第一行包含 1 个整数 n ,表示操场的顶点数和游戏的次数。 接下来有 N 行,每行包含两个整数 Xi 、Yi表示顶点的坐标。
输入保证按逆时针顺序输入点,所有点保证构成一个 n 多边形。所有点保证不存在三点共线。

输出格式

输出一个数,正确站位的概率,保留 4 位小数。

样例输入 1

5
1 8
0 7
0 0
8 0
8 8

样例输出 1

0.6316

样例输入 2

4
0 0
7 0
5 5
-1 4

样例输出 2

0.2475

提示

3≤N≤10^​5​​,−10^​9​​≤X,Y≤10^​9​​

题解

仅凭直觉和想象很难确定满足条件的点的位置(至少鄙人是这样……),这时候就需要用数学式帮助解题。根据题意,我们用叉乘求面积的方法(给点用叉乘,给边用海伦公式)写出一系列不等式(其实一组通式就够了),化简之后得到一组二元一次不等式。
在信息学中,我们通常用最短路(差分约束系统)来求解不等式组,然而本题只有两个变元且都带有系数,于是不能用图论算法解决。回忆在数学课上,我们通过线性规划的方法解二元一次不等式组,然而线性规划的过程不就是在求一个半平面交吗?于是本题就用半平面交解决。
注意一个小细节:鄙人使用直线左侧表示半平面,因此最后化简出的不等式都应大于零。

代码

#include<stdio.h>
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cmath>
using namespace std;
typedef double db;
const int maxn=1e5+5;
const db eps=1e-8;
int n,cnt,head,tail;
db area,ans;
struct Point{
    db x,y;
    Point(){}
    Point(db x,db y):x(x),y(y){}
}p[maxn],temp[maxn];
typedef Point Vector;
struct Line{
    Point P;
    Vector v;
    db ang;
    Line(){}
    Line(Point P,Vector v):P(P),v(v){ang=atan2(v.y,v.x);}
}L[2*maxn],q[2*maxn];
bool operator < (const Line& a,const Line& b)
{
    return a.ang<b.ang;
}
Point operator + (const Point& a,const Vector& b)
{
    return Point(a.x+b.x,a.y+b.y);
}
Vector operator - (const Point& a,const Point& b)
{
    return Vector(a.x-b.x,a.y-b.y);
}
Vector operator * (const Vector& a,const db& b)
{
    return Vector(a.x*b,a.y*b);
}
db Cross(Vector a,Vector b)
{
    return a.x*b.y-a.y*b.x;
}
bool Onleft(Line a,Point b)
{
    return Cross(a.v,b-a.P)>0;
}
Point GetIS(Line a,Line b)
{
    Vector u=a.P-b.P;
    db t=Cross(b.v,u)/Cross(a.v,b.v);
    return a.P+a.v*t;
}
void HPIS()
{
    sort(L+1,L+1+cnt);
    q[head=tail=1]=L[1];
    for(int i=2;i<=cnt;i++)
    {
        while(head<tail&&!Onleft(L[i],p[tail-1])) tail--;
        while(head<tail&&!Onleft(L[i],p[head])) head++;
        q[++tail]=L[i];
        if(fabs(Cross(q[tail].v,q[tail-1].v))<eps)
        {
            tail--;
            if(Onleft(q[tail],L[i].P)) q[tail]=L[i];
        }
        if(head<tail) p[tail-1]=GetIS(q[tail-1],q[tail]);
    }
    while(head<tail&&!Onleft(q[head],p[tail-1])) tail--;
    p[tail]=GetIS(q[head],q[tail]);
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lf%lf",&temp[i].x,&temp[i].y);
        if(i>1) L[++cnt]=Line(temp[i-1],temp[i]-temp[i-1]);
    }
    L[++cnt]=Line(temp[n],temp[1]-temp[n]),temp[++n]=temp[1];
    db tx1=temp[2].x-temp[1].x,ty1=temp[2].y-temp[1].y;
    for(int i=1;i<n;i++)
    {
        area+=Cross(temp[i],temp[i+1]);
        if(i>1)
        {
            db tx=temp[i+1].x-temp[i].x,ty=temp[i+1].y-temp[i].y;
            db A=ty-ty1,B=tx1-tx,C=ty1*temp[1].x+tx*temp[i].y-ty*temp[i].x-tx1*temp[1].y;
            if(fabs(A)<eps) L[++cnt]=Line(Point(0,-C/B),Vector(-B,A));
            else L[++cnt]=Line(Point(-C/A,0),Vector(-B,A));
        }
    }
    HPIS();
    for(int i=head;i<tail;i++) ans+=Cross(p[i],p[i+1]);
    ans+=Cross(p[tail],p[head]);
    printf("%.4lf\n",ans/area);
    return 0;
}
中描述了一个幼儿园里分配糖果的问题,每个小朋友都有自己的要求。问题的输入包括两个整数NN和KK,表示幼儿园里的小朋友数量和要满足的要求数量。接下来的KK行表示小朋友们的要求,每行有三个数字,XX,AA,BB。如果X=1,表示第AA个小朋友分到的糖果必须和第BB个小朋友分到的糖果一样多;如果X=2,表示第AA个小朋友分到的糖果必须少于第BB个小朋友分到的糖果;如果X=3,表示第AA个小朋友分到的糖果必须不少于第BB个小朋友分到的糖果;如果X=4,表示第AA个小朋友分到的糖果必须多于第BB个小朋友分到的糖果;如果X=5,表示第AA个小朋友分到的糖果必须不多于第BB个小朋友分到的糖果。这个问题可以被看作是一个差分约束系统的问题。 具体地说,可以使用差分约束系统来解决这个问题。差分约束系统是一种通过给变量之间的关系添加约束来求解最优解的方法。对于这个问题,我们需要根据小朋友们的要求建立约束条件,并通过解决这个约束系统来得出最小的糖果数量。 在问题的输入中,X的取值范围为1到5,分别对应不同的关系约束。根据这些约束,我们可以构建一个差分约束图。图中的节点表示小朋友,边表示糖果数量的关系。根据不同的X值,我们可以添加相应的边和权重。然后,我们可以使用SPFA算法(Shortest Path Faster Algorithm)来求解这个差分约束系统,找到满足所有约束的最小糖果数量。 需要注意的是,在读取输入时需要判断X和Y是否合法,即是否满足X≠Y。如果X=Y,则直接输出-1,因为这种情况下无法满足约束条件。 综上所述,为了满足每个小朋友的要求,并且满足所有的约束条件,我们可以使用差分约束系统和SPFA算法来求解这个问题。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [【差分约束系统】【SCOI2011】糖果 candy](https://blog.youkuaiyun.com/jiangzh7/article/details/8872699)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [P3275 [SCOI2011]糖果(差分约束板子)](https://blog.youkuaiyun.com/qq_40619297/article/details/88678605)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值