ACM Steps_Chapter Two_Section2

来源:互联网 发布:根域名 cname www 编辑:程序博客网 时间:2024/05/21 15:39

Fibonacci

/*用到了斐波那契数列的通项公式。先看对数的性质,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;}