1709 钉子和小球 1999年NOI全国竞赛
时间限制: 2 s
空间限制: 128000 KB
题目等级 : 大师 Master
题解
查看运行结果
题目描述 Description
有一个三角形木板,竖直立放,上面钉着n(n+1)/2颗钉子,还有(n+1)个格子(当n=5时如图1)。每颗钉子和周围的钉子的距离都等于d,每个格子的宽度也都等于d,且除了最左端和最右端的格子外每个格子都正对着最下面一排钉子的间隙。
让一个直径略小于d的小球中心正对着最上面的钉子在板上自由滚落,小球每碰到一个钉子都可能落向左边或右边(概率各1/2),且球的中心还会正对着下一颗将要碰上的钉子。例如图2就是小球一条可能的路径。
我们知道小球落在第i个格子中的概率pi= ,其中i为格子的编号,从左至右依次为0,1,...,n。
现在的问题是计算拔掉某些钉子后,小球落在编号为m的格子中的概率pm。假定最下面一排钉子不会被拔掉。例如图3是某些钉子被拔掉后小球一条可能的路径。
输入描述 Input Description
第1行为整数n(2<=n<=50)和m(0<=m<=n)。以下n行依次为木板上从上至下n行钉子的信息,每行中‘*’表示钉子还在,‘.’表示钉子被拔去,注意在这n行中空格符可能出现在任何位置。
输出描述 Output Description
仅一行,是一个既约分数(0写成0/1),为小球落在编号为m的格子中的概pm。既约分数的定义:A/B是既约分数,当且仅当A、B为正整数且A和B没有大于1的公因子。
样例输入 Sample Input
5 2
*
* .
* * *
* . * *
* * * * *
样例输出 Sample Output
7/16
数据范围及提示 Data Size & Hint
分类标签 Tags 点此展开
题解
f[i][j]表示落到第i层第j列的小球的个数。
如果(i,j)位置是‘*’时,它落到下一层左边和右边的几率是一样的,
f[i+1][j]+=f[i][j],f[i+1][j+1]+=f[i][j];
如果(i,j)位置是‘.’时,它直接落到下下层,因此他本该落到4个位置的球全部落到下下层,
f[i+2][j+1]+=(f[i][j]*4);
落到空隙的全部小球数目q=f[n+1][j](j从1到n+1)
最后将m+1(由于题目中m是从0开始数第m位,因此+1)位置的小球数目和全部小球数目q约分化简得到答案。
#include<cstdio> #include<iostream> using namespace std; long long f[60][60],k,q; char s[60][60]; int n,m; long long gcd(long long x,long long y)//辗转相除法,求最大公约数 { if (x%y==0) return y; else gcd(y,x%y); } int main() { cin>>n>>m; f[1][1]=1; for (int i=1;i<=n;i++) for (int j=1;j<=i;j++) { cin>>s[i][j]; if (s[i][j]=='*') { f[i+1][j]+=f[i][j]; f[i+1][j+1]+=f[i][j]; } else if (s[i][j]=='.') f[i+2][j+1]+=(f[i][j]*4); } for (int i=1;i<=n+1;i++) k+=f[n+1][i]; q=gcd(f[n+1][m+1],k); cout<<f[n+1][m+1]/q<<"/"<<k/q; }
附上一份wng的题解
根据概率求解
f(i,j)表示落在第i行第j列位置的概率
如果(i,j)位置是’*’, f(i+1,j)+=f(i,j)/2; f(i+1,j+1)+=f(i,j)/2;
如果(i,j)位置是’.’, f(i+2,j+1)+=f(i,j);
初始值:f(1,1)=1/1
f(n+1,m+1)即为所求。
/* 多次Wa的原因: 1<<n,应该(long long)1<<n */ #include <cstdio> #include <iostream> using namespace std; const int maxn=55; int n,m; long long f[maxn][maxn]; char ch; long long gcd(long long x,long long y){ if(x%y==0) return y; return gcd(y,x%y); } int main(){ scanf("%d%d",&n,&m); f[1][1]=(long long)1<<n; for(int row=1;row<=n;row++){ for(int col=1;col<=row;col++){ do scanf("%c",&ch); while(ch!='*'&&ch!='.'); if(ch=='*'){ f[row+1][col]+=f[row][col]/2; f[row+1][col+1]+=f[row][col]/2; }else f[row+2][col+1]+=f[row][col]; } } long long xi=gcd(f[n+1][m+1],(long long)1<<n); cout<<f[n+1][m+1]/xi<<"/"<<((long long)1<<n)/xi<<endl; return 0; }