ACM Steps_Chapter Two_Section3

A + B Problem II

/*
分析:对于此题做法有两种:其一,使2字符串的中的字符数字减去'0',
逐个相加大于等于10的可以使本位减10,下一位自增1,后面的处理就非常简单了;
其二,便是读入字符串后先让各个字符减'0',一一对应存入整形数组中;之后再相加。
对于2种方法大致是相同的,都要从后面向前加,逢十进位,以及数组初始化均要初始为0,
一边方便运算。
*/

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int main()
{
    int n,i,len1,len2,j,k,pi,t,flag;
    char str1[1010],str2[1010];
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        int a[1200]={0};
        flag=0;
        printf("Case %d:\n",i);
        scanf("%s%s",str1,str2);//以字符串形式读入
        len1=strlen(str1);
        len2=strlen(str2);
        printf("%s + %s = ",str1,str2);
        j=len1-1;
        k=len2-1;
        pi=0;
        while(j>=0&&k>=0)//开始相加
        {
            if(a[pi]+(str1[j]-'0')+(str2[k]-'0')>=10)//相加后大于10的
            {
                a[pi]=a[pi]+(str1[j]-'0')+(str2[k]-'0')-10;
                a[pi+1]++;
            }
            else
                a[pi]=a[pi]+(str1[j]-'0')+(str2[k]-'0');
            pi++;
            k--;
            j--;
        }
        if(j>=0)
        {
            for(t=j;t>=0;t--)
            {
                a[pi]=a[pi]+(str1[t]-'0');
                pi++;
            }
        }
        else if(k>=0)
        {
            for(t=k;t>=0;t--)
            {
                a[pi]=a[pi]+str2[t]-'0';
                pi++;
            }
        }
        else if(a[pi]!=0)//对于位数相同2个数加后最高位大于10的
            pi++;
        for(t=pi-1;t>=0;t--)
        {
            if(a[t]==0&&flag==0)//处理一次啊前导0,估计属于无用的一步
                continue;
            else
            {
                flag=1;
                printf("%d",a[t]);
            }
           
        }
        printf("\n");
        if(i!=n)//对于2组之间加空行的情况
            printf("\n");
    }
    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;
}

Hat's Fibonacci

/*
用二维数组模拟大数加法,每一行表述一个数,每一行的一个元素可以代表n位数,
这个可以根据自己的需要自己定义。
其他的就和正常的加法一样了,注意进位处理。
*/
#include <iostream>
#include <stdio.h>
using namespace std;
int s[7500][670];
void solve(){
    s[1][1] = 1;s[2][1] = 1;
    s[3][1] = 1;s[4][1] = 1;
    int i,j,k=0;
    for(i = 5;i<7500;i++)
    {
		for( j = 1;j<=670 ;j++)
    	{
        	k += s[i-1][j]+s[i-2][j]+s[i-3][j]+s[i-4][j];
        	s[i][j] = k%10000;
        	k = k/10000;
    	}
    	while(k)
    	{
        	s[i][j++] = k%10000;
        	k = k/10000;
    	}
	}
}
int main()
{
    int n,i,j;
    solve();
    while(cin>>n)
    {
        for(i = 670; i>=1;i--)
        if(s[n][i]!=0)break;
        printf("%d",s[n][i]);
        for(j = i-1;j>=1;j--)
        printf("%04d",s[n][j]);
        printf("\n");
    }
}

How Many Trees?

/*
Catalan数的解法

Catalan数的组合公式为 Cn=C(2n,n) / (n+1);
此数的递归公式为  h(n ) = h(n-1)*(4*n-2) / (n+1)
 小数解
对于50以下的小数解来说,使用数组就可以完成,代码如下:
#include<iostream>
using namespace std;
int main()
{
     long long  int a[101][101],i,j,n;
    for (i=0; i<101; i++)    //利用数组求 Catalan数
        a[i][0] = 1;
    for (i=1; i<101; i++)                    
    {
        for (j=1; j<=i; j++)   
            a[i][j] = a[i][j-1] + a[i-1][j];   
    }
    while (cin>>n)   
    {     
        cout<<a[n][n]<<endl;
    }
    return 0;
}
 大数解
对于大数来说,就应该使用下面的大数算法。
使用的公式为:h(n)  = h(n-1)*(4*n-2)/n+1;
*/
#include<iostream>
using namespace std;
#define MAX 100
#define BASE 10000
void multiply(int a[],int Max,int b)  //大数乘法
{
    int i,array=0;
    for (i=Max-1; i>=0; i--)   
    {
        array+=b*a[i];
        a[i] = array%BASE;
        array /= BASE;   
    }
}
 
void divide(int a[], int Max, int b)  //大数除法
{
    int i,div=0;
    for (i=0;i<Max; i++)   
    {
        div = div*BASE + a[i];
        a[i] = div / b;
        div %= b;
    }
}
int main()
{
    int a[101][MAX],i,j,n;
    memset(a[1],0,MAX*sizeof(int));
    for (i=2,a[1][MAX-1]=1; i<101; i++)
    {
        memcpy(a[i],a[i-1],MAX*sizeof(int));      //h[I] = h[i-1];  
        multiply(a[i],MAX,4*i-2);               //h[i] *= (4*i-2);
        divide(a[i],MAX,i+1);                  //h[i] /= (i+1);
    }
    while (cin>>n)   
    {
        for (i=0; i<MAX && a[n][i]==0; i++);  //去掉数组前为0的数字。
        cout<<a[n][i++];             //输出第一个非0数
        for (; i<MAX; i++)   
            printf("%04d",a[n][i]);       //输出后面的数,并每位都保持5位长度
        cout<<endl;
    }
    return 0;
}

Buy the Ticket

/*
推導過程如下:
m個人拿50,n個人拿100
1、如果n > m,那麼排序方法數為0,這一點很容易想清楚
2、現在我們假設拿50的人用‘0’表示,拿100的人用‘1’表示。
 如果有這麼一個序列0101101001001111。
 當第K個位置出現1的個數多餘0的個數時就是一個不合法的序列了
 假設m=4,n=3的一個序列是:0110100 。顯然,它不合法,現在我們把它稍微變化一下:
 把第二個1(這個1前面的都是合法的)後面的所有位0變成1,1變成0.
 就得到0111011這個序列1的數量多餘0的數量,顯然不合法,
 但現在的關鍵不是看這個序列是不是合法的
 關鍵是:他和我們的不合法序列0110100成一一對應的關係。
 也就是說任意一個不合法序列(m個0,n個1),都可以由另外一個序列(n-1個0和m+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)

推廣:
 如果原來有p張50元的話,那麼不合法的序列的數量應該是:
 任意一個不合法序列(m個0,n個1),都可以由另外一個序列(n-1個0和m+1+p個1)得到,
 所以是m+n個位置中,選擇m+1+p個位置,出來填上1所以是C(m+n,m+1+p),接下來簡化就不推了
 */


//卡特蘭數的應用
//公式如下:(m+n)! * (m-n+1) / (m+1) 
#include <iostream>
#include <string>
using namespace std;
int fact[205][100];
int res[100];
void multiply(int a[],int b)   //大數乘小數 
{
     int i,temp = 0;
     for(i = 99;i >= 0;i--)
     {
           temp += b * a[i];
           a[i] = temp % 10000;
           temp /= 10000;
     } 
}
void Fact()  //求階乘
{
     fact[0][99] = fact[1][99] = 1;
     for(int i = 2;i <= 200;i++)
     {
             memcpy(fact[i],fact[i-1],100*sizeof(int));
             multiply(fact[i],i);
     }
}
void divide(int a[],int b)       //大數除小數 
{
     int i,temp = 0;
     for(i = 0;i < 100;i++)
     {
           temp = temp*10000 + a[i];
           a[i] = temp / b;
           temp %= b;
     }
}
void output(int a[])       //輸出 
{
     int i = 0;
     while (i < 100 && a[i] == 0)
           i++;     //去掉前導的0 
     printf("%d",a[i++]);
     while(i < 100)
             printf("%04d",a[i++]);
     putchar('\n');
     
}
int main()
{
    int m,n,t = 1;
    Fact();
    while(cin >> m >> n , m+n)
    {
              printf("Test #%d:\n",t++);
              if(n > m)
              {
                   puts("0");
                   continue;
              }
              memcpy(res,fact[m+n],100*sizeof(int));
              multiply(res,m-n+1);
              divide(res,m+1);
              output(res);
    }
    return 0;
}

Count the Trees

/*
卡特蘭數的公式C(n) = ( 4n - 2 ) * C( n-1)   / (n+1)
這個相當於在卡特蘭數的基礎上乘以一個n!,即:
n!*C(n) = n! * (4n - 2) * C(n-1) / (n+1) = (4n - 2) * n * ((n-1) * C(n-1))  /  (n+1)
另H(n) = n! * C(n),則有: H(n) = (4n - 2) * n * H(n-1) / (n+1)
H(1) = 1*C(1) = 1
所以推導公式如下:
H(1) = 1;
H(n) = n * (4n - 2) * H(n-1) / (n+1)
*/
#include<iostream> 
using namespace std;
int a[101][1001] = {0};
int main()
{
    int n,i,j,k,b[101],len;
    b[1] = len = 1;
    a[1][0] = 1;
    for(i = 2;i < 101;i++)
    {
          for(j = 0;j < len;j++)    // 大數乘法
                a[i][j] = (4*i-2) * i * a[i-1][j];
          for(k = j = 0;j < len;j++)
          {
                a[i][j] += k;
                k = a[i][j] / 10;
                a[i][j] %= 10;
          }
          while(k) 
          {
                 a[i][len++] = k % 10;
                 k /= 10;
          }
          for(k = 0,j = len-1;j >= 0;j--)     //大數除法 
          {
                 a[i][j] += k*10;
                 k = a[i][j] % (i+1);
                 a[i][j] /= (i+1); 
          }
          while(!a[i][len-1])
                 len--;
          b[i] = len;
    }
    while(cin >> n,n)
    {
              for(i = b[n]-1;i >= 0;i--)
                    cout << a[n][i];
              cout << endl;
    }
    return 0;
} 

Game of Connections

/*
catalan数高精度

公式:c(n+1)=2*(2*n+1)*c(n)/(n+2)

高精度除法从高位开始模拟现实除法步骤求得商
*/
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>

using namespace std;

const int maxN=110;
const int maxM=1010;

int a[maxN][maxM];
int tmp[maxM];

int main(){
    a[0][0]=1;
    for(int i=0;i<maxN;++i){
        int k=2*(2*i+1);
        for(int j=0,c=0;j<maxM;++j){
            int s=a[i][j]*k+c;
            c=s/10;
            a[i+1][j]=s%10;
        }
        k=maxM;
        int cnt=0;
        while(!a[i+1][--k]);
        for(int s=0;k>=0;--k){
            s=10*s+a[i+1][k];
            tmp[cnt++]=s/(i+2);
            s%=(i+2);
        }
        for(int j=0;j<maxM;++j){
            if(j<cnt)
                a[i+1][j]=tmp[cnt-j-1];
            else
                a[i+1][j]=0;
        }
    }
    int n;
    while(cin>>n,n!=-1){
        int i=maxM;
        while(!a[n][--i]);
        for(;i>=0;i--)
            cout<<a[n][i];
        cout<<endl;
    }
    return 0;
}

小兔的棋盘

/*
令h(0) = 1,h(1) = 1,卡特蘭數滿足遞歸式:

h(n) = h(0) * h(n-1) + h(1) * h(n-2) + ... + h(n-1) * h(0) (其中n>=2),
這是n階遞推關係
還可以化簡為1階遞推關系:
h(0) = 1;
h(n) = (4n-2) / (n+1) * h(n-1) (n > 1) ;
該遞推關係的解為:h(n) = c(2n,n)/(n+1) (n = 1,2,3,...)
*/
//h(0) = 1;
//h(n) = (4n-2) * h(n-1) / (n+1) (n > 1) ;
//但是此題中如果直接用a[i] = (4*i-2)*a[i-1]/(i+1);會溢出 
//所以直接用这个式子
//h(n)=h(0)*h(n-1)+h(1)*h(n-2)+...+h(n-1)*h(0) 
#include<iostream> 
using namespace std;
__int64 a[36] = {0};
int main()
{
    int i,j,n,t=1;
    a[0] = 1;
    for(i = 1;i < 36;i++)
    {
          a[i] = 0;
          for(j = 0;j <= i;j++)
                a[i] += a[j] * a[i-j-1]; 
    }
    while(cin >> n && n != -1)
              cout << t++ << " " << n << " " << a[n]*2 << endl;
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值