您的位置:首页 > 其它

数学专题训练3

2014-02-28 20:15 134 查看
这次是概率专练

选的题都比较基础。。因为我对概率的感觉很不到位啊啊啊啊

题目一:wikioi计算概率

地址:http://www.wikioi.com/problem/1533/

这个就是古典概型嘛。。。先枚举选的第一根。。。然后再求出第选二根后和小于等于l的方案个数。然后答案很自然就是 这些方案个数的和/n*(n-1)了。。

考虑到n稍大。。求方案数不能直接暴力。。。可以选择二分来求(要注意选的第二根不能与第一根一样。。所以二分后还要判断下这些可选的木棍是否包含了第一根,包含了的话减去)

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<iostream>
using namespace std;
int n,ll;
int len[100000+10];
double ans=0;
int main()
{
scanf("%d%d",&n,&ll);
for(int i=1;i<=n;i++)scanf("%d",&len[i]);
sort(len+1,len+n+1);
for(int i=1;i<=n;i++)
{
int l=1,r=n;
while(l<r)
{
int mid=l+r+1>>1;
if(len[mid]+len[i]>ll)r=mid-1;
else l=mid;
}
if(l>=i)
{
l--;
}
if(l==1&&len[1]+len[i]>ll)l--;
ans=ans+l*1.0/(n-1);
}
printf("%.2f\n",ans/n);
return 0;
}


第二题:rqnoj矩阵粉刷

地址:http://www.rqnoj.cn/problem/717

rq的某次月赛题。。。当时我是第一次参加网赛。。结果一道都不会 T_T

其实这题的题意我觉得不是太清楚。。。说的随机选两个点。。。其实选的这两个点可以是一个。。然后对应的矩形就是一个小方格了

期望方块数自然就是 每个方块的被刷到的概率*权值1 的和。

但是要粉刷k次。。。不好直接求哪个小方格被刷的概率。。。于是可以求某次不被刷的概率 p,然后1-p^k就是被刷的概率啦

然后某次不被刷的概率又要用1-某次被刷的概率来求。。

考虑到这样一个问题:对于每个方块。。。包含了它的矩形一定有两个相对的顶点在它的坐上方和右下方。。。(包括它本身所在坐标)

然后这样的矩形数基本上就是 左上角点横坐标可选方案数*纵坐标可选方案数*右下角点横坐标可选方案数*纵坐标可选方案数。

对于不为一个或一行一列的矩形。。。有四种选点的方式会选到它,对于是一行或一列的矩形只有两种,对于只有一格的矩形(也就是正好是这一格本身)只统计了一次。。。

于是我们就枚举方块(i,j)

其一次被粉刷的方案数为 4*i*j*(w-i+1)*(h-j+1)-2*(i*(w-i+1)+j*(h-j+1))+1 (先全当成普通的矩形,然后减去为一行或一列时多计算的次数,再加上多扣了的当一个格子时的次数),概率就是 上面那个式子/w*w*h*h

这题基本上就这样了。。。

#include<cstdlib>
#include<cmath>
#include<cstring>
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
long long k,w,h;
double ans=0;
double qpow(double d,int c)
{
if(c==0)return 1;
if(c==1)return d;
double ret=qpow(d,c/2);
ret=ret*ret;
if(c&1)ret=ret*d;
return ret;
}
int main()
{
scanf("%lld%lld%lld",&k,&w,&h);
long long s=w*w*h*h;
for(int i=1;i<=w;i++)
{
for(int j=1;j<=h;j++)
{
double tem=i*(w-i+1)*j*(h-j+1)*4-2*(i*(w-i+1)+j*(h-j+1))+1;
tem=tem/s;
ans+=1-qpow(1-tem,k);
}
}
printf("%.0f\n",ans);
return 0;
}


题目三:rqnoj bomb

地址:http://www.rqnoj.cn/problem/691

我觉得稍微有点高端的题。。。纠结了很久(当然对大神们来说还是水到爆啦)

这道题是一道与期望有关的dp

状态这么设计:dp[i]表示剩i个精灵时的期望操作次数(我开始设计成减少i个的期望。。。结果纠结半天)

然后就可以得出这样一个方程:

dp[i]=(dp[i]*p[0]+dp[i-1]*p[1]+dp[i-2]*p[2]+....+dp[i-5]*p[5])+1;

直接用这个方程来递推的话。。。当然不行。。。因为左右都有dp[i];

所以移个项整理一下...变成dp[i]=p[1]/(1-p[0])*dp[i-1]+...+p[5]/(1-p[0])*dp[i-5]+1/(1-p[0]);

边界是这样的:对于i<m,dp[i]=0;

考虑到数据范围有点大。。。这个递推又是线性的。于是就可以想到用矩阵乘法来优化。。。

然后基本就没什么难点了

#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
int n,m;
double p[10];
double end=0;
struct Mat
{
int x,y;
double num[8][8];
Mat(){memset(num,0,sizeof(num));}
Mat operator *(const Mat &b)
{
Mat c;
c.x=x;c.y=b.y;
for(int i=1;i<=x;i++)
{
for(int j=1;j<=b.y;j++)
for(int k=1;k<=y;k++)
{
c.num[i][j]+=num[i][k]*b.num[k][j];
}
}
return c;
}
}start,single,ele;
void readdata()
{
scanf("%d%d",&n,&m);
for(int i=0;i<=m;i++)scanf("%lf",&p[i]);
}
double dp[5+10];
void pre()
{
start.x=6;start.y=1;
dp[m]=1.0/(1-p[0]);
for(int i=m+1;i<=4;i++)
{
for(int j=1;j<=5;j++)if(i-j>=0)dp[i]+=(dp[j]*p[i-j]/(1-p[0]));
dp[i]+=1.0/(1-p[0]);
}
start.num[1][1]=dp[4];
start.num[2][1]=dp[3];
start.num[3][1]=dp[2];
start.num[4][1]=dp[1];
start.num[5][1]=dp[0];
start.num[6][1]=1.0/(1-p[0]);
single.x=single.y=6;
for(int i=1;i<=6;i++)
{
single.num[i][i]=1;
}
for(int i=1;i<=5;i++)ele.num[1][i]=p[i]/(1-p[0]);
ele.x=ele.y=6;
ele.num[1][6]=1;ele.num[2][1]=ele.num[3][2]=ele.num[4][3]=ele.num[5][4]=1;
ele.num[6][6]=1;
}
Mat qpow(const Mat &d,int c)
{
if(c==0)return single;
if(c==1)return d;
Mat ret=qpow(d,c/2);
ret=ret*ret;
if(c&1)ret=ret*d;
return ret;
}
int main()
{
readdata();
pre();//计算出dp[0]到dp[4]并存入start
if(n<=4)printf("%.1f\n",dp
);
else
{
Mat ans=qpow(ele,n-4);
ans=ans*start;
printf("%.1f\n",ans.num[1][1]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: