POJ1189我初看觉得是一道很容易的题目,但是我的思考比较局限,想出来的动态规划过程并不是十分完美
对任意一行,假设 “-”代表空格“*”代表钉子,
比方说某一行是 -*-*-*-,我使用了一个数组记录每个空格位置和钉子位置出现小球的概率
定义了一个分数加法
我采用了人人为我的形式
计算球出现在这一行钉子上的概率,等于球出现在上一行左右两个钉子上的概率分别除以2相加,再加上球出现在这个钉子正上方的空格的概率
计算球出现在这一行空格上的概率,如果这个空格上方没钉子,概率就直接是正上方钉子出现球的概率,有钉子,则是0
最后输出最后一行的对应位置的概率即可
后来我参考了别人的代码,感觉自己的方法十分复杂
1.不仅要记录球出现在钉子上的概率,也要记录球出现在空格上的概率
2.每步都要分数加减,加上分数加减的函数代码长到1200B,看起来很不爽
不过好在这个方法是成功的,我从中学到了
1.memset只能初始化0或-1,初始化成别的数字会出现错误,因为它是用八位的二进制数字逐八位覆盖数组的全部空间,一个int是32位,如果初始化成0,就是 00000000 00000000 000000000 000000000,初始化成1,就会变成000000001 000000001 0000000001 0000000001,就不是1了。
2.long long的printf要对应%lld,一开始怎么都输出错,后来才发现
3.一行写了个gcd,当然我知道大家都会,但是我还是挺激动的
网上参考了别人的方法,使用我为人人的方法做的,某一行遍历,存在钉子,给下一行对应位置加上相应概率,没有钉子,则概率直接传到下下行的钉子上,不用计算空格上的概率。
而且他假设一开始有2^n个钉子,这就避免了过程中的小数出现,我感觉是一种很机智的做法,就是要注意1<<N应该写成1LL<<N否则会出现错误。
我的代码
#include<stdio.h>
#include<memory.h>
long long mark[52][105];
long long deno[52][105];
long long num[52][105];
int N;
int m;
long long resDeno;
long long resNum;
long long gcd(long long m,long long n){
return n==0?m:gcd(n,m%n);
}
void fractionAdd(long long deno1,long long num1,long long deno2,long long num2){
resDeno=deno1*deno2;
resNum=num2*deno1+num1*deno2;
long long gc=gcd(resDeno,resNum);
resDeno/=gc;
resNum/=gc;
}
int main(){
scanf("%d %d",&N,&m);
char c;
memset(mark,0,sizeof(mark));
for(int i=1;i<=N;i++){
for(int j=1;j<=i;j++){
scanf(" %c",&c);
if(c=='*'){
mark[i][j*2]=1;
}
}
}
memset(num,0,sizeof(num));
for(int i=0;i<=51;i++){
for(int j=0;j<=104;j++){
deno[i][j]=1;
}
}
num[1][2]=1;
for(int i=2;i<=N+1;i++){
for(int j=1;j<=2*i+1;j++){
if(j%2==0){
fractionAdd(2*deno[i-1][j-2],num[i-1][j-2]*mark[i-1][j-2],2*deno[i-1][j],num[i-1][j]*mark[i-1][j]);
fractionAdd(resDeno,resNum,deno[i-1][j-1],num[i-1][j-1]);
num[i][j]=resNum;
deno[i][j]=resDeno;
}
else{
if(mark[i-1][j-1]==0){
num[i][j]=num[i-1][j-1];
deno[i][j]=deno[i-1][j-1];
}
else{
num[i][j]=0;
deno[i][j]=1;
}
}
}
}
printf("%lld/%lld\n",num[N+1][m*2+2],deno[N+1][m*2+2]);
return 0;
}
根据网上的思路改变的代码
#include<stdio.h>
#include<memory.h>
long long mark[52][52];
long long num[52][52];
int N;
int m;
long long gcd(long long m,long long n){
return n==0?m:gcd(n,m%n);
}
int main(){
scanf("%d %d",&N,&m);
char c;
memset(mark,0,sizeof(mark));
for(int i=1;i<=N;i++){
for(int j=1;j<=i;j++){
scanf(" %c",&c);
if(c=='*'){
mark[i][j]=1;
}
}
}
memset(num,0,sizeof(num));
num[1][1]=1LL<<N;
for(int i=1;i<=N+1;i++){
for(int j=1;j<=i;j++){
if(mark[i][j]==1){
num[i+1][j]+=num[i][j]>>1;
num[i+1][j+1]+=num[i][j]>>1;
}
else{
num[i+2][j+1]+=num[i][j];
}
}
}
long long deno=0;
for(int i=1;i<=N+1;i++){
deno+=num[N+1][i];
}
long long gc=gcd(deno,num[N+1][m+1]);
printf("%lld/%lld\n",num[N+1][m+1]/gc,deno/gc);
return 0;
}