zoukankan      html  css  js  c++  java
  • ACM Steps_Chapter Two_Section2

    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.010063744
    10^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; //人数减1
    				if(j <= n) 
    					break;
    				else	
    					sum--;
    				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.1415926
    int 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;
    }
    


  • 相关阅读:
    chapter01 Bob'store
    echo拼接
    《PHP和MySql Web》书,<input>属性
    http协议
    asc18_hpl,hpc,hpcg
    考研北邮总结[转发共享]
    考研经验总结【转发共享】
    ISBN号码 201312-2
    出现次数最多的数 201312-1
    相反数 201403-1
  • 原文地址:https://www.cnblogs.com/oldoldb/p/3311324.html
Copyright © 2011-2022 走看看