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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章