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