2019牛客暑期多校训练营(第一场)F题随机数做法

新技能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) 36xS(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 11的正方形内随机撒n个点,问n个点中两点最小距离的期望是多少?
    思路:随机数模拟,当n很大时,必然会出现很多距离小于 1 0 − 3 10^{-3} 103的情况,精度误差很小,就不要管了直接输出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方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值