/*
分析:对于此题做法有两种:其一,使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;
}