POJ1189钉子和小球,人人为我 我为人人 longlong 位操作

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;
}

猜你喜欢

转载自blog.csdn.net/luo3300612/article/details/78405193