题型:计算几何
题意:
一个平面上有无数个相距为D的平行直线,一个直径为D的均匀圆盘上有N个点组成的多边形,随机将圆盘扔在平面上,求平面上的某直线穿过多边形的概率。
分析:
首先分析圆盘上只有一条线段的情况,设线段长为l。若线段与平面上直线垂直,则线段与直线相交的概率如图1
如果旋转一下角度,概率就变为如图2所示
那么随机扔线段与直线相交的概率为
对于n个点,先求一个凸包。
凸包的每一条边与直线相交的概率都是
那么总概率就是概率之和,但是考虑到凸包的一个性质,即:任意一条直线穿过凸包,都会与两条边长相交,所以总概率是
所以求一个凸包周长即可。
但是在比赛过程中,对于我等脑残弱智党来说,推出上述公式简直难,俺们会的就是暴力了。。。
于是想到枚举精度,将180度切成若干分,每次度数加一点算出一个l,随后算一个平均值就是概率。
然后,我就开始暴力提交了,我将180度切成3000分卡过。
公式正解代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define LL long long
#define mt(a,b) memset(a,b,sizeof(a))
using namespace std;
const double eps = 1e-10;
const double PI = acos(-1.0);
struct point {
double x,y;
} p[123],res[123];
int n,d;
bool operator < (const point &l, const point &r) {
return l.y < r.y || (fabs(l.y- r.y)<eps && l.x < r.x);
}
class ConvexHull { //凸包
bool mult(point sp, point ep, point op) {
return (sp.x - op.x) * (ep.y - op.y)>= (ep.x - op.x) * (sp.y - op.y);
}
public:
int graham(int n,point p[],point res[]) {//多边形点个数和点数组,凸包存res
sort(p, p + n);
if (n == 0) return 0;
res[0] = p[0];
if (n == 1) return 1;
res[1] = p[1];
if (n == 2) return 2;
res[2] = p[2];
int top=1;
for (int i = 2; i < n; i++) {
while (top && mult(p[i], res[top], res[top-1])) top--;
res[++top] = p[i];
}
int len = top;
res[++top] = p[n - 2];
for (int i = n - 3; i >= 0; i--) {
while (top!=len && mult(p[i], res[top],res[top-1])) top--;
res[++top] = p[i];
}
return top; // 返回凸包中点的个数
}
} tu;
double caldis(point A,point B) {
return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));
}
double Perimeter() {
double ans = caldis(res[n-1],res[0]);
for(int i=0; i<n-1; i++) {
ans += caldis(res[i],res[i+1]);
}
return ans;
}
int main() {
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int cas = 0,_;
scanf("%d",&_);
while(_--) {
scanf("%d%d",&n,&d);
for(int i=0; i<n; i++) {
scanf("%lf%lf",&p[i].x,&p[i].y);
}
n = tu.graham(n,p,res);
double c = Perimeter();
// printf("C = %.4f\n",c);
printf("Case #%d: %.4f\n",++cas,c/(PI*d));
}
return 0;
}
枚举精度代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#define LL long long
#define mt(a,b) memset(a,b,sizeof(a))
using namespace std;
const double eps = 1e-10;
const double PI = acos(-1.0);
const double inf = 1e8;
struct point {
double x,y;
} p[123],res[123];
int n,d;
bool operator < (const point &l, const point &r) {
return l.y < r.y || (fabs(l.y- r.y)<eps && l.x < r.x);
}
class ConvexHull { //凸包
bool mult(point sp, point ep, point op) {
return (sp.x - op.x) * (ep.y - op.y)>= (ep.x - op.x) * (sp.y - op.y);
}
public:
int graham(int n,point p[],point res[]) {//多边形点个数和点数组,凸包存res
sort(p, p + n);
if (n == 0) return 0;
res[0] = p[0];
if (n == 1) return 1;
res[1] = p[1];
if (n == 2) return 2;
res[2] = p[2];
int top=1;
for (int i = 2; i < n; i++) {
while (top && mult(p[i], res[top], res[top-1])) top--;
res[++top] = p[i];
}
int len = top;
res[++top] = p[n - 2];
for (int i = n - 3; i >= 0; i--) {
while (top!=len && mult(p[i], res[top],res[top-1])) top--;
res[++top] = p[i];
}
return top; // 返回凸包中点的个数
}
} tu;
double caldis(point A,point B) {
return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));
}
double Perimeter() {
double ans = caldis(res[n-1],res[0]);
for(int i=0; i<n-1; i++) {
ans += caldis(res[i],res[i+1]);
}
return ans;
}
struct line {
point a,b;
};
point intersection(point u1,point u2,point v1,point v2) {
point ret=u1;
double t=((u1.x-v1.x)*(v1.y-v2.y)-(u1.y-v1.y)*(v1.x-v2.x)) /((u1.x-u2.x)*(v1.y-v2.y)-(u1.y-u2.y)*(v1.x-v2.x));
ret.x+=(u2.x-u1.x)*t;
ret.y+=(u2.y-u1.y)*t;
return ret;
}
point ptoline(point p,line l) {///点到直线上的最近点
point t=p;
t.x+=l.a.y-l.b.y,t.y+=l.b.x-l.a.x;
return intersection(p,t,l.a,l.b);
}
int main() {
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int cas = 0,_;
scanf("%d",&_);
while(_--) {
scanf("%d%d",&n,&d);
for(int i=0; i<n; i++) {
scanf("%lf%lf",&p[i].x,&p[i].y);
}
n = tu.graham(n,p,res);
///90度枚举3000次
double zl = 180.0 / 3000;
double l;
double sum = 0.00;
for(double i=0; i<180; i+=zl) {
point S,T;///S为左下点,T为右上点
double cita = i * PI / 180.0;
for(int j=0; j<n; j++) {
point tmp;
double k = tan(cita);
line ll;
ll.a.x = 0.0;
ll.a.y = 0.0;
ll.b.x = 1.0;
ll.b.y = k;
tmp = ptoline(res[j],ll);
if(j==0) {
S.x = tmp.x;
S.y = tmp.y;
T.x = tmp.x;
T.y = tmp.y;
}
S = min(S,tmp);
T = max(T,tmp);
}
l = caldis(S,T);
sum+=l;
}
// printf("sum = %.4f\n",sum);
printf("Case #%d: %.4f\n",++cas,sum/3000/d);
}
return 0;
}