/*用到了斐波那契数列的通项公式。先看对数的性质,loga(b^c)=c*loga(b),loga(b*c)=loga(b)+loga(c);假设给出一个数10234432,那么log10(10234432)=log10(1.0234432*10^7)=log10(1.0234432)+7;log10(1.0234432)就是log10(10234432)的小数部分.log10(1.0234432)=0.01006374410^0.010063744=1.023443198那么要取几位就很明显了吧~先取对数(对10取),然后得到结果的小数部分bit,pow(10.0,bit)以后如果答案还是<1000那么就一直乘10。注意偶先处理了0~20项是为了方便处理~这题要利用到数列的公式:an=(1/√5) * [((1+√5)/2)^n-((1-√5)/2)^n](n=1,2,3.....)取完对数log10(an)=-0.5*log10(5.0)+((double)n)*log(f)/log(10.0)+log10(1-((1-√5)/(1+√5))^n)其中f=(sqrt(5.0)+1.0)/2.0;log10(1-((1-√5)/(1+√5))^n)->0所以可以写成log10(an)=-0.5*log10(5.0)+((double)n)*log(f)/log(10.0);最后取其小数部分。*/#include<iostream>#include<cstdio>#include<cmath>#include<algorithm>using namespace std;int f[21] = {0, 1, 1};int main(){int n;for(int i = 2; i < 21; ++i)f[i] = f[i - 1] + f[i - 2];while(scanf("%d", &n) != EOF){if(n <= 20){printf("%d\n", f[n]);continue;}else{double temp = -0.5 * log(5.0) / log(10.0) + ((double)n) * log((sqrt(5.0)+1.0)/2.0) / log(10.0);temp -= floor(temp);temp = pow(10.0, temp);while(temp < 1000)temp *= 10;printf("%d\n", (int)temp);}}return 0;}
Joseph
#include<iostream>#include<cstdio>#include<cstring>#include<iostream>#include<cmath>using namespace std;int res[16];int fun(int n){int ans, flag, sum;if(res[n])return res[n];else{for(ans = n + 1; ; ++ans){flag = 0;sum = 2 * n;for(int j = ans; ; j += ans - 1) //人数减1{if(j > sum)j = j % sum ? j % sum : sum; //人数减1if(j <= n) break;elsesum--;if(sum == n){flag = 1;break;}}if(flag){res[n] = ans;return res[n];}}}}int main(){int n;while(scanf("%d", &n) && n){printf("%d\n", fun(n));}return 0;}
汉诺塔VII
/*对一个含有n个盘子,从A柱移动到C柱借助B柱的汉诺塔,第n个盘子移动到C柱过程是这样子的:首先将其余的n-1个盘子移动到B柱,然后第n个盘子直接移动到C柱。在这过程中,第n个盘子只出现在A柱和C柱两个柱子上,也即第n个盘子不可能出现在B柱上。因此对于当前移动的盘子,只需检查其所在的柱子是否符合这个要求,如果出现在B柱上,则显然进入了错误移动中。这是本题求解思想精髓所在。汉诺塔是个递归求解过程,假设第n个盘子符合要求,则判别的下一个目标是第n-1个盘子。若第n个盘子在A柱上,此时剩余n-1个盘子必由A柱移动到B柱,经由C柱。此时对第n-1个盘子,C柱就是其不可能出现的位置;若第n个盘子在C住上,这剩余n-1个盘子则是在B柱上,经由A柱,移动到C柱上,因此,A柱就是第n-1个盘子不可能出现的位置。*/#include<iostream>using namespace std;bool flag;void DFS(int n,int *A,int *B,int *C){ if(n==0){ flag = true; return ; } if(B[0]&&n==B[1]){ flag = false; return ; } if(A[0]&&n==A[1]){ A[1]=A[0]-1; DFS(n-1,++A,C,B); }else if(C[0]&&n==C[1]){ C[1]=C[0]-1; DFS(n-1,B,A,++C); }}int main(){ int A[70],B[70],C[70]; int T,n; cin>>T; while(T--){ cin>>n; cin>>A[0]; for(int i=1;i<=A[0];i++) cin>>A[i]; cin>>B[0]; for(int i=1;i<=B[0];i++) cin>>B[i]; cin>>C[0]; for(int i=1;i<=C[0];i++) cin>>C[i]; DFS(n,A,B,C); if(flag) cout<<"true"<<endl; else cout<<"false"<<endl; }}
Wolf and Rabbit
#include<cstdio>#include<cstdlib>#include<cstring>bool gcd(int m,int n){while(n%m){int temp=n%m;n=m;m=temp;}return m==1?false:true;}int main(){int m,n;int p;scanf("%d",&p);for(int i=0;i<p;i++){scanf("%d%d",&m,&n);printf("%s\n",gcd(m,n)?"YES":"NO");}return 0;}
献给杭电五十周年校庆的礼物
/*①n条直线把平面分割成的区域数: f(n)=f(n-1)+n=n(n+1)/2+1;②把空间分割为最多的区域数的时候,第n个平面与前(n-1)个平面相交,且无三面共线,所以此时该平面与前(n-1)个平面有(n-1)条交线。这些交线把第n个平面分割为f(n-1)个区域,于是这个平面将原有空间一分为二,故增加了f(n-1)个空间,得递推公式:g(n)=g(n-1)+f(n-1)=(n^3+5n)/6+1。*/#include<iostream>using namespace std;int main(){ int n; while(cin>>n) cout<<(n*n*n+5*n+6)/6<<endl; return 0;}
Train Problem II
/*出栈次序一个栈(无穷大)的进栈序列为1,2,3,…,n,有多少个不同的出栈序列?[4-5] 常规分析首先,我们设f(n)=序列个数为n的出栈序列种数。同时,我们假定,从开始到栈第一次出到空为止,这段过程中第一个出栈的序数是k。特别地,如果栈直到整个过程结束时才空,则k=n首次出空之前第一个出栈的序数k将1~n的序列分成两个序列,其中一个是1~k-1,序列个数为k-1,另外一个是k+1~n,序列个数是n-k。此时,我们若把k视为确定一个序数,那么根据乘法原理,f(n)的问题就等价于——序列个数为k-1的出栈序列种数乘以序列个数为n - k的出栈序列种数,即选择k这个序数的f(n)=f(k-1)×f(n-k)。而k可以选1到n,所以再根据加法原理,将k取不同值的序列种数相加,得到的总序列种数为:f(n)=f(0)f(n-1)+f(1)f(n-2)+……+f(n-1)f(0)。看到此处,再看看卡特兰数的递推式,答案不言而喻,即为f(n)=h(n)= C(2n,n)/(n+1)= c(2n,n)-c(2n,n+1)(n=0,1,2,……)。最后,令f(0)=1,f(1)=1。非常规分析对于每一个数来说,必须进栈一次、出栈一次。我们把进栈设为状态‘1’,出栈设为状态‘0’。n个数的所有状态对应n个1和n个0组成的2n位二进制数。由于等待入栈的操作数按照1‥n的顺序排列、入栈的操作数b大于等于出栈的操作数a(a≤b),因此输出序列的总数目=由左而右扫描由n个1和n个0组成的2n位二进制数,1的累计数不小于0的累计数的方案种数。在2n位二进制数中填入n个1的方案数为c(2n,n),不填1的其余n位自动填0。从中减去不符合要求(由左而右扫描,0的累计数大于1的累计数)的方案数即为所求。不符合要求的数的特征是由左而右扫描时,必然在某一奇数位2m+1位上首先出现m+1个0的累计数和m个1的累计数,此后的2(n-m)-1位上有n-m个 1和n-m-1个0。如若把后面这2(n-m)-1位上的0和1互换,使之成为n-m个0和n-m-1个1,结果得1个由n+1个0和n-1个1组成的2n位数,即一个不合要求的数对应于一个由n+1个0和n-1个1组成的排列。反过来,任何一个由n+1个0和n-1个1组成的2n位二进制数,由于0的个数多2个,2n为偶数,故必在某一个奇数位上出现0的累计数超过1的累计数。同样在后面部分0和1互换,使之成为由n个0和n个1组成的2n位数,即n+1个0和n-1个1组成的2n位数必对应一个不符合要求的数。因而不合要求的2n位数与n+1个0,n-1个1组成的排列一一对应。显然,不符合要求的方案数为c(2n,n+1)。由此得出输出序列的总数目=c(2n,n)-c(2n,n+1)=c(2n,n)/(n+1)=h(n+1)。*/#include<iostream>using namespace std;int a[101][101]={0};int main(){ int n,i,j,len,r,temp,t; int b[101]; a[1][0] = 1; len = 1; b[1] = 1; for(i=2;i<=100;i++) { t = i-1; for(j=0;j<len;j++) //乘法 a[i][j] = a[i-1][j]*(4*t+2); for(r=j=0;j<len;j++) //处理相乘结果 { temp = a[i][j] + r; a[i][j] = temp % 10; r = temp / 10; } while(r) //进位处理 { a[i][len++] = r % 10; r /= 10; } for(j=len-1,r=0;j>=0;j--) //除法 { temp = r*10 + a[i][j]; a[i][j] = temp/(t+2); r = temp%(t+2); } while(!a[i][len-1]) //高位零处理 len --; b[i] = len; } while(cin>>n) { for(j=b[n]-1;j>=0;j--) printf("%d",a[n][j]); printf("\n"); } return 0;}
Examining the Rooms
/*首先这题其实让我们求的是给 N个元素,让我们求K个环排列的 方法数。斯特林第一类数的第推公式:S(N,0)=0;S(N,N)=1;S(0,0)=0;S(N,K)=S(N-1,K-1)+S(N-1,K)*(N-1);这个公式的意思是:当前N-1个数构成K-1 个环的时候,加入第N个 ,N只能构成单环!—S(N-1,K-1)如果N-1个数构成K个环的时候,加入第N个,N可以任意加入,N-1内的一个环里,所以是–(N-1)*S(N-1,K)这个题目里,因为不能破坏第1个门,所以S(N,K)-S(N-1,K-1)才是能算构成K个环的方法数!就是去掉1自己成环的情况!*/#include<iostream>#include<cstdio>using namespace std;const int N = 21;long long ans[N][N],f[N];int main(){int t,n,k;ans[1][1]=1;f[0]=f[1]=1;for(int i=2;i<N;i++){for(int j=1;j<=i;j++){ans[i][j]=ans[i-1][j-1]+(i-1)*ans[i-1][j];}f[i]=f[i-1]*i;}cin>>t;while(t--){cin>>n>>k;long long sum=0;for(int i=1;i<=k;i++){sum+=ans[n][i]-ans[n-1][i-1];}printf("%.4lf\n",(double)sum/f[n]);}system("pause");return 0;}
Big Number
/*我们有公式, log10(n!)=(0.5*log(2*PI*n)+n*log(n)-n)/log(10) , 这里利用了 log10 (N)=ln(N)/ln(10);公式的名字是 斯特林公式*/#include<stdio.h>#include<stdlib.h>#include<math.h>#include<string.h>#include<time.h>#define PI 3.1415926int main(){ int T; scanf("%d",&T); while(T--) { int digits,n; scanf("%d",&n); if(n==0) //没有也过了.. { printf("1\n"); continue; } digits=(int)((0.5*log(2*PI*n)+n*log(n)-n)/log(10)); printf("%d\n",digits+1); //加1是必须的。 } return 0;}