<textarea cols="120" rows="100" name="code" class="cpp"> //poj 1189 钉子和小球 记忆搜索 /* 题意:基于DP的思想,每一个位置(i,j)的球可能来自三个地方(如果存在):(i-1,j-1),(i-1,j),(i-2,j-1), 递归算出三个地方的概率再加起来就行了。 PS:此题加深了我对大数乘法溢出的警戒心。 */ #include <iostream> using namespace std; const int inf = 1<<28; int n,m,k; char str[1000000]; int s[52][52]; __int64 dp[52][52][2]; __int64 gcd(__int64 n,__int64 m) { __int64 r; if (n<m) swap(n,m); if (m==0) return n; while (r=n%m) { n=m; m=r; } return m; } //分数加法,ai为分母,bi为分子 void add(__int64 &b1,__int64 &a1,__int64 b2,__int64 a2) { if (b2==0) return ; if (b1==0) { b1=b2,a1=a2; return ;} __int64 L=a1/gcd(a1,a2)*a2; //此处要先除再乘,不然可能会溢出 __int64 temp=b1*(L/a1)+b2*(L/a2); //此处同理 __int64 g=gcd(temp,L); b1=temp/g; a1=L/g; } void cal(int i,int j) { //记忆搜索 if (dp[i][j][0]!=-1){ return ; } if (i==0) { dp[i][j][0]=dp[i][j][1]=1; return ;} dp[i][j][0]=0;dp[i][j][1]=1;//初始0 //以下是三个决策 if (i-1>=0 && j-1>=0 && j-1<i && s[i-1][j-1]) { cal(i-1,j-1); add(dp[i][j][0],dp[i][j][1],dp[i-1][j-1][0],dp[i-1][j-1][1]*2); } if (i-1>=0 && j<i && s[i-1][j]) { cal(i-1,j); add(dp[i][j][0],dp[i][j][1],dp[i-1][j][0],dp[i-1][j][1]*2); } if (i-2>=0 && j-1>=0 && j-1<i-1 && !s[i-2][j-1]) { cal(i-2,j-1); add(dp[i][j][0],dp[i][j][1],dp[i-2][j-1][0],dp[i-2][j-1][1]); } } int main() { while (scanf("%d%d",&n,&m)!=EOF) { memset(s,0,sizeof(s)); getchar(); for (int i=0;i<n;i++) { gets(str); k=-1; for(int j=0;str[j];++j) if (str[j]!=' ') { ++k; if (str[j]=='*') s[i][k]=1; } } memset(dp,-1,sizeof(dp)); cal(n,m); printf("%lld/%lld/n",dp[n][m][0],dp[n][m][1]); } system("pause"); return 0; } </textarea>