新技能get,以前不知道可以用随机数来找规律,也叫蒙特卡洛算法。还是太年轻了TAT。
如何使用c++的随机数
- 2019牛客暑期多校训练营(第一场)F
题意: 给一个三角形,顶点为a,b,c随机在三角形内找一个点p,得到的贡献为
三角形面积S(abp),S(pbc),S(apc)中最大的,求贡献的期望。
思路:题目提醒了输出答案时乘以36.,所以这个期望应该是 x 36 ∗ S ( a b c ) \frac{x}{36}*S(abc) 36x∗S(abc),x是未知的,我们用rand()函数随机生成p点,如果p点在三角形内就计算贡献,否则就不管了,最后估算x的值。
判断点是否在三角形内有很多方法,这里推荐两种:
1.用面积判断点是否在三角行内部
2.用叉积判断
struct Point{
double x;
double y;
};
double cross(const Point &a, const Point &b, const Point &p) //叉乘
{
return (b.x - a.x)*(p.y - a.y) - (b.y - a.y)*(p.x - a.x);
}
bool toLeft(const Point &a, const Point &b, const Point &p) //判断p是否是在ab的左边
{
return cross(a, b, p) > 0;
}
bool inTriangle(const Point &p, const Point &a, const Point &b, const Point &c)
{
bool res = toLeft(a, b, p);
if (res != toLeft(b, c, p))
return false;
if (res != toLeft(c, a, p))
return false;
if (cross(a, b, c) == 0) //ABC is in one line
return false;
return true;
}
最后我们找到近似值x=22.
估算x代码:
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N=1e7;
struct point{
double x,y;
}a[4],p;
double cal_S(point a,point b,point c){//计算三角形面积
return fabs(a.x*b.y+b.x*c.y+c.x*a.y-a.x*c.y-b.x*a.y-c.x*b.y)/2.0;
}
bool intri(point a,point b,point c,point p){//判断是否在三角形内
return cal_S(a,b,c)==cal_S(p,b,c)+cal_S(a,p,c)+cal_S(a,b,p);
}
int main(){
srand((unsigned)time(0));
for(int i=1;i<=3;i++)a[i].x=(double)(rand()%N),a[i].y=(double)(rand()%N);
double ans=0.0;
int cnt=0;
for(int i=1;i<=N;i++){//循环次数越多结果越准
p.x=(double)(rand()%N),p.y=(double)(rand()%N);//生成点p的坐标
if(intri(a[1],a[2],a[3],p)){//判断随机生成的点是否在三角形内
ans+=max(cal_S(p,a[2],a[3]),max(cal_S(a[1],p,a[3]),cal_S(a[1],a[2],p)))/cal_S(a[1],a[2],a[3]);
cnt++;//统计计算次数
}
}
cout<<"实验期望:"<<ans/(double)cnt<<endl;
for(int i=1;i<=35;i++)cout<<i<<"/36: "<<(double)i/36.0<<endl;
}
练习:
- EOJ 3658 清点星辰
题意:在 1 ∗ 1 1*1 1∗1的正方形内随机撒n个点,问n个点中两点最小距离的期望是多少?
思路:随机数模拟,当n很大时,必然会出现很多距离小于 1 0 − 3 10^{-3} 10−3的情况,精度误差很小,就不要管了直接输出0 (这种思路在很多题目中出现)。
#include <bits/stdc++.h>
using namespace std;
struct point{
double x,y;
}p[1100];
double cal_d(point a,point b){
return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}
int main(){
int n;
cin>>n;
if(n>1000){
//这种思路经常在某些题目里出现,当某些数太大时,精度误差很小,就不要管了
cout<<"0.000\n";
return 0;
}
srand(0);
double ans=0.0;
int T=1e7/n/n;//实验次数
for(int k=1;k<=T;k++){
for(int i=1;i<=n;i++){
p[i].x=(double)(rand()%32767)/32766.0;//生成0-1的小数
p[i].y=(double)(rand()%32767)/32766.0;
}
double temp=100000.0;
for(int i=1;i<=n;i++)
for(int j=i+1;j<=n;j++)
temp=min(temp,cal_d(p[i],p[j]));
ans+=temp;
}
ans/=(double)T;//除以实验的次数
printf("%.8lf\n",ans);
}
这个题是
n
2
n^{2}
n2的方法求的n个点,两点之间的最短距离但其实还可以优化一下(不过没必要)。
计算最近距离的n*logn方法