Buy the Ticket(高精度--乗算、除算、階乗)
3107 ワード
タイトルを手に入れたばかりの頃はまだ簡単な感じでしたが、その間ずっと並び合わせで作り、50 および100 グループとして見て、長い間やっていて、間違っていて、しかも複雑で、しかも最初は高精度であることを意識していなかったし、この問題がこんなに巧みに解けるとは思わなかった.実は今までその真髄がよく分からなかったので、後でゆっくり理解しよう.でも、この問題は高精度の典型的な問題とすることができる.
正確に言えば、高精度を捨ててこの問題は完全に数学の問題です...
もちろんこれはカトラン数の変式問題です(カトラン数について:http://baike.baidu.com/linkurl=fyZozBGxxnRSFNKavxpOEc_0ldvwOEVp7ic1GZ8mb1GlKPp4GPuFE86NR1pD7JqyznX1L_IAxhf3ehYicO_N5q)
繰返し式は簡単に得られる
F[i][j]=F[i-1][j]+F[i][j-1];(i問題解決の考え方:(参考)http://www.cnblogs.com/one--world--one--dream/archive/2011/09/30/2196839.html )
50を持っている人は「0」、100を持っている人は1と仮定します.
もしこのようなシーケンス0101101001001111があるならば.......K番目の位置に1の個数が余分な0の個数が現れたときは1つの非合法なシーケンスですm=4 n=3のシーケンスを仮定すると:0110100は明らかに非合法で、今私たちはそれを少し変えます:2番目の1(この1の前のものはすべて合法です)後のすべてのビット0が1になり、1が0になると0111011というシーケンス1の数が0より多い数が得られ、明らかに合法ではないが、現在の鍵はこのシーケンスが合法かどうかを見ることではない.それは私たちの非合法なシーケンス0110100と1つ1つに対応する関係、すなわち任意の非合法なシーケンス(m個0,n個1)であり、他のシーケンスであってもよい(n−1個の0とm+1個の1)また,1つのシーケンスが合法的であるか,あるいは非合法的であるかを知るため,合法シーケンス数=シーケンス総数−非合法シーケンスの総量シーケンス総数は,m+n個の位置のうち,n個の位置を選択して1を記入することができ,C(m+n,n)非合法シーケンスの数は:m+n個の位置の中で、m+1個の位置を選択して1を記入するのでC(m+n,m+1)で、そして誰もが異なるので、全配列m!*nが必要です!
だから最後の公式は(C(m+n,n)−C(m+n,m+1)*m!*n! 簡略化後(m+n)!*(m-n+1)/(m+1);
次は高精度の実現です.コードは各機能靴が完備しています.
テーマの出所:http://acm.hdu.edu.cn/showproblem.php?pid=1133
正確に言えば、高精度を捨ててこの問題は完全に数学の問題です...
もちろんこれはカトラン数の変式問題です(カトラン数について:http://baike.baidu.com/linkurl=fyZozBGxxnRSFNKavxpOEc_0ldvwOEVp7ic1GZ8mb1GlKPp4GPuFE86NR1pD7JqyznX1L_IAxhf3ehYicO_N5q)
繰返し式は簡単に得られる
F[i][j]=F[i-1][j]+F[i][j-1];(i
50を持っている人は「0」、100を持っている人は1と仮定します.
もしこのようなシーケンス0101101001001111があるならば.......K番目の位置に1の個数が余分な0の個数が現れたときは1つの非合法なシーケンスですm=4 n=3のシーケンスを仮定すると:0110100は明らかに非合法で、今私たちはそれを少し変えます:2番目の1(この1の前のものはすべて合法です)後のすべてのビット0が1になり、1が0になると0111011というシーケンス1の数が0より多い数が得られ、明らかに合法ではないが、現在の鍵はこのシーケンスが合法かどうかを見ることではない.それは私たちの非合法なシーケンス0110100と1つ1つに対応する関係、すなわち任意の非合法なシーケンス(m個0,n個1)であり、他のシーケンスであってもよい(n−1個の0とm+1個の1)また,1つのシーケンスが合法的であるか,あるいは非合法的であるかを知るため,合法シーケンス数=シーケンス総数−非合法シーケンスの総量シーケンス総数は,m+n個の位置のうち,n個の位置を選択して1を記入することができ,C(m+n,n)非合法シーケンスの数は:m+n個の位置の中で、m+1個の位置を選択して1を記入するのでC(m+n,m+1)で、そして誰もが異なるので、全配列m!*nが必要です!
だから最後の公式は(C(m+n,n)−C(m+n,m+1)*m!*n! 簡略化後(m+n)!*(m-n+1)/(m+1);
次は高精度の実現です.コードは各機能靴が完備しています.
テーマの出所:http://acm.hdu.edu.cn/showproblem.php?pid=1133
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <iostream>
#define MAX 102
using namespace std;
int factor[205][MAX]={0}; //factor[i] i , factor[i][] ,
int result[201]={0}; //
void multiply(int s[],int Max,int b)//s[]*b
{
int ans=0;
for(int i=Max;i>=0;i--)
{
ans=ans+s[i]*b;
s[i]=ans%10000;
ans=ans/10000;
}
}
void div(int s[],int Max,int b)// s[]/b
{
int ans=0;
for(int i=0;i<=Max;i++)
{
ans=ans*10000+s[i];
s[i]=ans/b;
ans%=b;
}
}
int getfactor(){ //
int i;
factor[0][MAX-1]=factor[1][MAX-1]=1;
for(i=2;i<=203;i++){
memcpy(factor[i],factor[i-1],MAX*sizeof(int));//this has a falut that i have replace memcpy by strcpy!
multiply(factor[i],MAX-1,i);
}
return 0;
}
int output(int *s,int k){ //
int i=1;
printf("Test #%d:
",k);
while(s[i]==0&&i<MAX)
i++;
printf("%d",s[i++]);
for(;i<MAX;i++)
printf("%04d",s[i]);
printf("
");
return 0;
}
int main()
{
int m,n,i,k=1;
getfactor();
while(scanf("%d %d",&m,&n),m+n){
if(n>m){ // 100 50
printf("Test #%d:
",k++);
printf("0
");// , BUG ,5555....
continue;
}
memcpy(result,factor[m+n],sizeof(int)*MAX);// (m+n)! result
multiply(result,MAX-1,m-n+1); // (m+n)!*(m-n+1)
div(result,MAX-1,m+1); // (m+n)!*(m-n+1)/(m+1)
output(result,k);
k++;
}
return 0;
}