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;
}