hdu - 4327 - Shooting - 立体几何 + 半平面交

http://acm.hdu.edu.cn/showproblem.php?pid=4327

转自http://blog.csdn.net/haha593572013/article/details/7829075

题意:给你一些点( x, y ), 0 < x < 1 , 0 < y < 1.再给你一个概率分布函数,表示每个座标点的概率分布,显然整个矩形的分布概率之和为1
另外有一个规则:每个点都只管辖离自己最近的区域,即和其他所有点的中垂线所切割成的区域。
求每块区域分布的概率是多少
解法:显然,每个点所管辖的区域可以用这个点与其他点得中垂线去切割平面,这个点所在的一侧为有效半平面,所以要先判断点与直线的方位再去切割平面,这里我写了两个切割的函数,
容易看出,题目所给的概率分布函数是空间中的一个平面,所以可以想象以半平面切割后的区域为底,最顶上是一个斜面的立体(这个空间立体要仔细想想)
那么这个立体的体积就是我们要求的答案。
具体在求的时候可以先求出底下棱柱的体积,最上面的那个角单独计算。
咋一看,最上面的部分什么形状都没有,没办法算啊,仔细一看就知道了,可以将它切割成若干个四棱锥,锥顶始终是最矮的那个点,这个需要大家发挥空间想象能力。

#include <cmath>
#include <cstdio>
#include<algorithm>
using namespace std;
const  double INF = 1000000000.0;
const  int maxn = 1010;
const double eps = 1e-12;
inline double sgn(double x) {return fabs(x)<eps?0:(x>0?1:-1);}
struct Point{
    double x,y;
    Point(double tx=0,double ty=0){x=tx;y=ty;}
    bool operator == (const Point& t) const {
        return sgn(x-t.x)==0 && sgn(y-t.y)==0;
    }
     Point operator - (const Point& t) const {
        Point tmp;
        tmp.x = x - t.x;
        tmp.y = y - t.y;
        return tmp;
    }
}p[maxn],tmp[maxn],pp[maxn],GP;
struct Seg{Point s,e;};
struct Line {
    double a, b, c;
};//中垂线
double cross(Point a,Point b,Point c){return (b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x);}
inline Point intersect(Point x,Point y,double a,double b,double c){  
    double u = fabs(a * x.x + b * x.y + c);  
    double v = fabs(a * y.x + b * y.y + c);  
    return Point( (x.x * v + y.x * u) / (u + v) , (x.y * v + y.y * u) / (u + v) );  
}  
int n,ban_tot;
void CUT1(double a,double b,double c){//points must be clockwise    a*x+b*y+c>=0表示点在半平面内   
    int i,tot=0;
    for(int i = 1; i <= ban_tot; ++i){  
        if(a*p[i].x + b*p[i].y + c >= eps) pp[++tot] = p[i];  
        else {  
            if(a*p[i-1].x + b*p[i-1].y + c > eps){  
                pp[++tot] = intersect(p[i],p[i-1],a,b,c);  
            }  
            if(a*p[i+1].x + b*p[i+1].y + c > eps){  
                pp[++tot] = intersect(p[i],p[i+1],a,b,c);  
            }  
        }  
    }  ban_tot=tot;
    pp[tot+1]=pp[1];pp[0]=pp[tot];
    memcpy(p,pp,sizeof(pp));
}
void CUT2(double a,double b,double c){//a*x+b*y+c<=0表示点在半平面内        
    int i,tot=0;
    for(int i = 1; i <= ban_tot; ++i){  
        if(!(a*p[i].x + b*p[i].y + c > eps) ) pp[++tot] = p[i]; //点在半平面内或者边界上 
        else {  
            if(a*p[i-1].x + b*p[i-1].y + c < -eps){  //上个点完全在半平面内
                pp[++tot] = intersect(p[i],p[i-1],a,b,c);  
            }  
            if(a*p[i+1].x + b*p[i+1].y + c < -eps){  //下个点完全在半平面内
                pp[++tot] = intersect(p[i],p[i+1],a,b,c);  
            }  
        }  
    }  ban_tot=tot;
    pp[tot+1]=pp[1];pp[0]=pp[tot];//两端都扩展一个点
    memcpy(p,pp,sizeof(pp));
}
Line Turn(Point s, Point e) {                                        // 线段转直线表达式
    Line ln;
    ln.a = s.y - e.y;
    ln.b = e.x - s.x;
    ln.c = s.x*e.y - e.x*s.y;
    return ln;
}
Line make(Point a,Point b)//求a,b中垂线的直线方程
{
    double x0=(a.x+b.x)/2;
    double y0=(a.y+b.y)/2;
    Line tmp=Turn(a,b);
    Line ans;
    ans.a=tmp.b;
    ans.b=-tmp.a;
    ans.c=tmp.a*y0-tmp.b*x0;
    return ans;
}
Line ln[maxn];
inline double PPdis(Point a, Point b) {                                // 点点距离
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
inline double PLdis(Point p,Point l1,Point l2){                        // 点线距离
    return fabs(cross(p,l1,l2))/PPdis(l1,l2);
}
double calc(Point *p,int n)
{
      if(n<3) return 0;
      double area=0,V=0;
      for(int i=0;i<n;i++) area+=p[(i+1)%n].y*(p[i].x-p[(i+2)%n].x);
      area/=2;
      area=fabs(area);
      double H=10;
      int pos=0;
      for(int i=0;i<n;i++)
      {
          if(2-p[i].x-p[i].y<H)
          {
                  H=2-p[i].x-p[i].y;
                  pos=i;
          }
      }
      V+=area*H;
      for(int i=pos+1;;i++)
      {  
          int id1=i%n;
          int id2=(i+1)%n;
          if(id2==pos) break;
          double h=PLdis(p[pos],p[id1],p[id2]);
          double s=((2-p[id1].x-p[id1].y-H) + (2-p[id2].x-p[id2].y-H)) * PPdis(p[id1],p[id2])/2;
          V+=s*h/3;
      }
      return V;
}
double ans[110];
int main(){
    int t,ca=1,n;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%lf%lf",&tmp[i].x,&tmp[i].y);
        for(int i=1;i<=n;i++)
        {
            p[1]=Point(0,0);
            p[2]=Point(0,1);
            p[3]=Point(1,1);
            p[4]=Point(1,0);
            p[0]=p[4];
            p[5]=p[1];//首尾都要加上一个点,保证边界的合法点不会漏掉
			ban_tot=4;
            double x0=tmp[i].x,y0=tmp[i].y;
            for(int j=1;j<=n;j++)
            {
                if(i==j) continue;
                Line mid_line = make(tmp[i],tmp[j]);
                double a=mid_line.a,b=mid_line.b,c=mid_line.c;
                if(a*x0+b*y0+c >= eps)
                {
                    CUT1(a,b,c);
                }
                else 
                {
                    CUT2(a,b,c);
                }
            }
            double tmpv=calc(p,ban_tot);
            ans[i]=tmpv;
        }
        printf("Case #%d:\n",ca++);
        for(int i=1;i<=n;i++)
        {
            printf("%.6lf\n",ans[i]);
        }
    }
    return 0;
}


另一个人的博客, 看着计算几何, 真难搞。

一开始看到distribution第一印象就是概率分布函数,结果经数学院大牛和Clarification的提醒,发现原来是概率密度函数。

半平面交求得每一个点的凸包,接下来就是对概率的计算。
这里只考虑三角形的积分,


先考虑左边的三角形,
y的上界:k1 = (y1 - y0) / (x1 - x0), y = k1x - k1x0 + y0
y的下界:k2 = (y2 - y0) / (x2 - x0), y = k2x - k2x0 + y0
对三角形求积分,先对y求积分,
f = int(2 - x - y, y, k2 * x - k2 * x0 + y0, k1 * x - k1 * x0 + y0)
= -((k1 - k2)*(x - x0)*(2*x + 2*y0 + k1*x + k2*x - k1*x0 - k2*x0 - 4))/2
接着对x求积分:
int(f, x, x0, x1)
= (k1^2*x0^3)/6 - (k1^2*x1^3)/6 - (k2^2*x0^3)/6 + (k2^2*x1^3)/6 + k1*x0^2 - (k1*x0^3)/6 + k1*x1^2 - k2*x0^2 - (k1*x1^3)/3 + (k2*x0^3)/6 - k2*x1^2 + (k2*x1^3)/3 + (k1*x0*x1^2)/2 - (k2*x0*x1^2)/2 - (k1*x0^2*y0)/2 - (k1*x1^2*y0)/2 + (k2*x0^2*y0)/2 + (k2*x1^2*y0)/2 + (k1^2*x0*x1^2)/2 - (k1^2*x0^2*x1)/2 - (k2^2*x0*x1^2)/2 + (k2^2*x0^2*x1)/2 - 2*k1*x0*x1 + 2*k2*x0*x1 + k1*x0*x1*y0 - k2*x0*x1*y0
= -((k1 - k2)*(x0 - x1)^2*(x0 + 2*x1 + 3*y0 - k1*x0 + k1*x1 - k2*x0 + k2*x1 - 6))/6

接着考虑右边的三角形,
y的上界:k1 = (y1 - y0) / (x1 - x0), y = k1x - k1x0 + y0
y的下界:k2 = (y2 - y0) / (x2 - x0), y = k2x - k2x0 + y0
对y求积分:
f = int(2 - x - y, y, k2 * x - k2 * x0 + y0, k1 * x - k1 * x0 + y0)
= -((k1 - k2)*(x - x0)*(2*x + 2*y0 + k1*x + k2*x - k1*x0 - k2*x0 - 4))/2
对x求积分:
int(f, x, x1, x0)
= (k1^2*x1^3)/6 - (k1^2*x0^3)/6 + (k2^2*x0^3)/6 - (k2^2*x1^3)/6 - k1*x0^2 + (k1*x0^3)/6 - k1*x1^2 + k2*x0^2 + (k1*x1^3)/3 - (k2*x0^3)/6 + k2*x1^2 - (k2*x1^3)/3 - (k1*x0*x1^2)/2 + (k2*x0*x1^2)/2 + (k1*x0^2*y0)/2 + (k1*x1^2*y0)/2 - (k2*x0^2*y0)/2 - (k2*x1^2*y0)/2 - (k1^2*x0*x1^2)/2 + (k1^2*x0^2*x1)/2 + (k2^2*x0*x1^2)/2 - (k2^2*x0^2*x1)/2 + 2*k1*x0*x1 - 2*k2*x0*x1 - k1*x0*x1*y0 + k2*x0*x1*y0
= ((k1 - k2)*(x0 - x1)^2*(x0 + 2*x1 + 3*y0 - k1*x0 + k1*x1 - k2*x0 + k2*x1 - 6))/6

结果显而易见,只差了了个负号。概率的结果肯定是正数,直接加个取绝对值就行了。

对于一个凸包总能拆分成这样的三角形……然后就能求出来了……

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int MAXN = 200;
const double eps = 1e-12;

int dblcmp(double x)
{
    if(fabs(x) < eps)
    {
        return 0;
    }
    return x > 0 ? 1 : -1;
}

struct Point
{
    double x, y;
    inline void input()
    {
        scanf("%lf%lf",&x,&y);
    }
    inline void output()
    {
        printf("%.2lf %.2lf\n", x, y);
    }
    bool operator < (const Point &point) const
    {
        if(y == point.y)
        {
            return x < point.x;
        }
        return y < point.y;
    }
}key[MAXN], poly[MAXN];
int n, polyNumber;

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

Point operator - (const Point &x, const Point &y)
{
    Point r;
    r.x = x.x - y.x;
    r.y = x.y - y.y;
    return r;
}

bool operator == (const Point &a, const Point &b)
{
    return fabs(a.x - b.x) < eps && fabs(a.y - b.y) < eps;
}

struct Line
{
    Point a, b;
    double ang;
}line[MAXN], stack[MAXN];
int lineNumber, stackTop;

bool operator < (const Line &x, const Line &y)
{
    if(fabs(x.ang - y.ang) < eps)
    {
        return (y.b - x.a) * (x.b - y.a) > eps;
    }
    return x.ang < y.ang;
}

Point operator * (const Line &x, const Line &y)
{
    double a1 = (y.b - x.a) * (y.a - x.a);
    double a2 = (y.a - x.b) * (y.b - x.b);
    Point r;
    r.x = (x.a.x * a2 + x.b.x * a1) / (a1 + a2);
    r.y = (x.a.y * a2 + x.b.y * a1) / (a1 + a2);
    return r;
}

double cross(Point &a, Point &b, Point &o)
{
    return (a.x - o.x) * (b.y - o.y) - (a.y - o.y) * (b.x - o.x);
}

bool judgeOut(const Line &x, const Point &p)
{
    return (p - x.a) * (x.b - x.a) > eps;
}

bool isParellel(const Line &x, const Line &y)
{
    return fabs((x.b - x.a) * (y.b - y.a)) < eps;
}

double getArea(Point p[], int n)
{
    double ans = 0.0;
    for(int i=2;i<n;++i)
    {
        ans += fabs(cross(p[i], p[i-1], p[0]));
    }
    return ans * 0.5;
}

void halfPlane()
{
    polyNumber = 0;
    sort(line, line + lineNumber);
    stackTop = 1;
    int bottom = 0, tmp = 1;
    for(int i=1;i<lineNumber;++i)
    {
        if(line[i].ang - line[i-1].ang > eps)
        {
            line[tmp++] = line[i];
        }
    }
    lineNumber = tmp;
    stack[0] = line[0], stack[1] = line[1];
    for(int i=2;i<lineNumber;++i)
    {
        if(isParellel(stack[stackTop], stack[stackTop-1]) || isParellel(stack[bottom], stack[bottom+1]))
        {
            return;
        }
        while(bottom < stackTop && judgeOut(line[i], stack[stackTop] * stack[stackTop - 1]))
        {
            -- stackTop;
        }
        while(bottom < stackTop && judgeOut(line[i], stack[bottom] * stack[bottom + 1]))
        {
            ++ bottom;
        }
        stack[++stackTop] = line[i];
    }
    while(bottom < stackTop && judgeOut(stack[bottom], stack[stackTop] * stack[stackTop - 1]))
    {
        -- stackTop;
    }
    while(bottom < stackTop && judgeOut(stack[stackTop], stack[bottom] * stack[bottom + 1]))
    {
        ++ bottom;
    }
    if(stackTop <= bottom + 1)
    {
        return;
    }
    stack[++stackTop] = stack[bottom];
    for(int i=bottom;i<stackTop;++i)
    {
        poly[polyNumber ++] = stack[i] * stack[i+1];
    }
    int k = 1;
    for(int i=1;i<polyNumber;++i)
    {
        if(dblcmp(poly[i].x - poly[i-1].x) != 0 || dblcmp(poly[i].y - poly[i-1].y) != 0)
        {
            poly[k++] = poly[i];
        }
    }
    polyNumber = k;
}

double getIntegral(Point p0, Point p1, Point p2)
{
    double k1 = (p1.y - p0.y) / (p1.x - p0.x);
    double k2 = (p2.y - p0.y) / (p2.x - p0.x);
    double res = (k1 - k2) * (p0.x - p1.x) * (p0.x - p1.x) * (p0.x + p1.x * 2 + p0.y * 3 - k1 * p0.x + k1 * p1.x - k2 * p0.x + k2 * p1.x - 6) / 6;
    return fabs(res);
}

void addLine(double x0, double y0, double x1, double y1)
{
    line[lineNumber].a.x = x0;
    line[lineNumber].a.y = y0;
    line[lineNumber].b.x = x1;
    line[lineNumber].b.y = y1;
    line[lineNumber].ang = atan2(y1 - y0, x1 - x0);
    ++ lineNumber;
}

double calc(int index)
{
    lineNumber = 0;
    addLine(0.0, 0.0, 1.0, 0.0);
    addLine(1.0, 0.0, 1.0, 1.0);
    addLine(1.0, 1.0, 0.0, 1.0);
    addLine(0.0, 1.0, 0.0, 0.0);
    Point mid, lp;
    for(int i=0;i<n;++i)
    {
        if(i != index)
        {
            mid.x = (key[i].x + key[index].x) * 0.5;
            mid.y = (key[i].y + key[index].y) * 0.5;
            if(dblcmp(key[i].y - key[index].y) == 0)
            {
                lp.x = mid.x;
                lp.y = mid.y + 1.0;
            }
            else
            {
                double k = - (key[i].x - key[index].x) / (key[i].y - key[index].y);
                lp.x = mid.x + 1.0;
                lp.y = mid.y + k;
            }
            if(cross(lp, mid, key[index]) > 0)
            {
                swap(mid, lp);
            }
            addLine(mid.x, mid.y, lp.x, lp.y);
        }
    }
    halfPlane();
    double ans = 0.0;
    Point p[3], pc;
    Line l[2];
    for(int i=2;i<polyNumber;++i)
    {
        p[0] = poly[i];
        p[1] = poly[i-1];
        p[2] = poly[0];
        for(int j=0;j<3;++j)
        {
            for(int k=j+1;k<3;++k)
            {
                if(p[j].x > p[k].x)
                {
                    swap(p[j], p[k]);
                }
            }
        }
        if(dblcmp(p[0].x - p[1].x) == 0)
        {
            if(p[0].y > p[1].y)
            {
                ans += getIntegral(p[2], p[0], p[1]);
            }
            else
            {
                ans += getIntegral(p[2], p[1], p[0]);
            }
        }
        else if(dblcmp(p[2].x - p[1].x) == 0)
        {
            if(p[1].y > p[2].y)
            {
                ans += getIntegral(p[0], p[1], p[2]);
            }
            else
            {
                ans += getIntegral(p[0], p[2], p[1]);
            }
        }
        else
        {
            l[0].a = p[1];
            l[0].b = p[1];
            l[0].b.y -= 1.0;
            l[1].a = p[0];
            l[1].b = p[2];
            pc = l[0] * l[1];
            if(p[1].y > pc.y)
            {
                ans += getIntegral(p[0], p[1], pc);
                ans += getIntegral(p[2], p[1], pc);
            }
            else
            {
                ans += getIntegral(p[0], pc, p[1]);
                ans += getIntegral(p[2], pc, p[1]);
            }
        }
    }
    return ans;
}

int main()
{
    int t;
    scanf("%d", &t);
    for(int cas=1;cas<=t;++cas)
    {
        scanf("%d", &n);
        for(int i=0;i<n;++i)
        {
            key[i].input();
        }
        printf("Case #%d:\n", cas);
        for(int i=0;i<n;++i)
        {
            printf("%.6lf\n", calc(i));
        }
    }
    return 0;
}


发布了177 篇原创文章 · 获赞 2 · 访问量 18万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章