step小结2

来源:互联网 发布:局域网流量分析软件 编辑:程序博客网 时间:2024/06/16 01:34

第二章

P1

素数(公约数)

素数用约定俗成的筛法就能得出结果。然而当数据过大时,普通的筛法提交会TLE,需要对其进行优化。因为偶数(几乎)全都不是素数,可以只考虑奇数。先将序列离散化一下。之后在使用筛法进行筛选。

参考代码:

1. half=SIZE/2;   

2. int sn = (int) sqrt(SIZE);   

3. for (i = 0; i < half; i++)   

4.    p[i] = true;// 初始化全部奇数为素数。p[0]对应3,即p[i]对应2*i+3/2*i+1   

5. for (i = 0; i < sn; i++) {      

6. if(p[i])//如果 i+i+3/i+i+1 是素数  

7. {       

8.     for(k=i+i+3, j=k*i+k+i; j < half; j+=k)   

9.     // 筛法起点是 p[i]所对应素数的平方 k^2                                          

10.     // k^2在 p 中的位置是 k*i+k+i  

11.     //    下标 i         k*i+k+i  

12.     //对应数值 k=i+i+3   k^2           

13.        p[j]=false;   

14. }   

15. }   

P2

斐波那契数列,卡特兰数

2.2.1

数学题,推不出公式。。。只能看其他大神的博客记下来

斐波那契公式 an=(1/√5) * [((1+√5)/2)^n-((1-√5)/2)^n](n=1,2,3.....)

运用了对数的性质loga(b^c)=c*loga(b),loga(b*c)=loga(b)+loga(c)

对公式取对数

最后要注意题意得输出情况,酌情使用公式

代码:

#include<iostream>

#include<cstring>

#include<cstdio>

#include<cmath>

#include<algorithm>

#define N 21

using namespace std;

int main(){

    int a[N];

    a[0]=0;a[1]=1;

    for(int i=2;i<N;i++)

        a[i]=a[i-1]+a[i-2];

    int n;double s;

    while(scanf("%d",&n)!=EOF){

        if(n<20){

            cout<<a[n]<<endl;

        }else{

            s=log10(1.0/sqrt(5))+n*log10((1.0+sqrt(5))/2.0)+4-(int)(log10(1.0/sqrt(5))+n*log10((1.0+sqrt(5))/2.0)+1);

            int ans=(int)pow(10.0,s);

            cout<<ans<<endl;

        }

    }

    return 0;

}

卡特兰数

原理

h(0)=1,h(1)=1catalan数满足递推式[1]  

h(n)= h(0)*h(n-1)+h(1)*h(n-2) + ... + h(n-1)*h(0) (n>=2)

例如:h(2)=h(0)*h(1)+h(1)*h(0)=1*1+1*1=2

h(3)=h(0)*h(2)+h(1)*h(1)+h(2)*h(0)=1*2+1*1+2*1=5

另类递推式[2]  

h(n)=h(n-1)*(4*n-2)/(n+1);

递推关系的解为:

h(n)=C(2n,n)/(n+1) (n=0,1,2,...)

递推关系的另类解为:

h(n)=c(2n,n)-c(2n,n-1)(n=0,1,2,...)

重点在于应用

一直没理解这些应用之间的联系,好在只有这几种情况。一一记下。

(转自百度百科)

1.括号化问题。

  矩阵链乘: P=a1×a2×a3×……×an,依据乘法结合律,不改变其顺序,只用括号表示成对的乘积,试问有几种括号化的方案?(h(n)种)

2.出栈次序问题。

  一个栈(无穷大)的进栈序列为1,2,3,..n,有多少个不同的出栈序列?

  类似:有2n个人排成一行进入剧场。入场费5元。其中只有n个人有一张5元钞票,另外n人只有10元钞票,剧院无其它钞票,问有多少中方法使得只要有10元的人买票,售票处就有5元的钞票找零?(将持5元者到达视作将5元入栈,持10元者到达视作使栈中某5元出栈)

3.将多边行划分为三角形问题。

  将一个凸多边形区域分成三角形区域的方法数?

  类似:一位大城市的律师在她住所以北n个街区和以东n个街区处工作。每天她走2n个街区去上班。如果她

  从不穿越(但可以碰到)从家到办公室的对角线,那么有多少条可能的道路?

  类似:在圆上选择2n个点,将这些点成对连接起来使得所得到的n条线段不相交的方法数?

4.给顶节点组成二叉树的问题。

  给定N个节点,能构成多少种不同的二叉树?

 

 

卡特兰数模板:

#include<iostream>

#include<cstring>

#include<cstdio>

#include<cmath>

#define MAX 100

#define BASE 10000

using namespace std;

void cheng(int a[],int max,int b){

    int ans=0;int i;

    for(i=max-1;i>=0;i--){

        ans+=a[i]*b;

        a[i]=ans%BASE;

        ans/=BASE;

    }

}

void chu(int a[],int max,int b){

    int div=0;

    int i;

    for(i=0;i<max;i++){

        div=div*BASE+a[i];

        a[i]=div/b;

        div=div%b;

    }

}

int main(){

    int n,i;

    int a[101][MAX];

    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));

        cheng(a[i],MAX,4*i-2);

        chu(a[i],MAX,i+1);

    }

    while(scanf("%d",&n)!=EOF){

        if(n==-1)break;

        for (i = 0; i < MAX && a[n][i] == 0; i++); //去掉数组前为0的数字。

            cout << a[n][i++];

        for(;i<MAX;i++){

            printf("%04d",a[n][i]);

        }

        cout<<endl;

    }

    return 0;

}

 

模板中使用了大数间的乘除法

2.2.6

斯特林数

概念:

第一类Stirling数表示表示将 n 个不同元素构成m个圆排列的数目。又根据正负性分为无符号第一类Stirling数su(n,m)和带符号第一类Stirling数ss(n,m) 。有无符号Stirling数分别表现为其升阶函数和降阶函数的各项系数[类似于二项式系数],形式如下:

 

s(n,m)=(n-1)*s(n-1,m)+s(n-1,m-1) ,1<=m<=n-1

 

SN0=0; SNN=1 S00=0 SNK=SN-1K-1+SN-1K*N-1(高中数学没学好。。)

代码如下:

//斯特林数

#include<iostream>

#include<cstring>

#include<cstdio>

#include<cmath>

#include<algorithm>

#define LL long long

using namespace std;

LL sum[21];

LL s[21][21];

int main(){

    sum[0]=0;sum[1]=1;

    for(int i=2;i<=20;i++){

        sum[i]=i*sum[i-1];

    }

    for(int i=1;i<=20;i++){

        s[i][0]=0;

        s[i][i]=1;

        for(int j=1;j<i;j++){

            s[i][j]=s[i-1][j-1]+(i-1)*s[i-1][j];

        }

    }

    int t;

    cin>>t;

    int n,k;

    while(t--){

        cin>>n>>k;

        if(n==1||k==0){

            printf("0.0000\n");

            continue;

        }else{

            LL ans=0;

            for(int i=1;i<=k;i++){

                ans+=(s[n][i]-s[n-1][i-1]);

            }

            printf("%0.4lf\n",(ans*1.0)/sum[n]);//tips:这里ans*1.0是为了将其从LL转为long double

        }

    }

    return 0;

}

 

斯特林公式

用途:斯特林公式(Stirling's approximation)是一条用来取n阶乘近似值的数学公式。一般来说,当n很大的时候,n阶乘的计算量十分大,所以斯特林公式十分好用,而且,即使在n很小的时候,斯特林公式的取值已经十分准确。

公式:

res=(long)( (log10(sqrt(4.0*acos(0.0)*n)) + n*(log10(n)-log10(exp(1.0)))) + 1 )

代码:

#include<iostream>

#include<cstring>

#include<cstdio>

#include<cmath>

#include<algorithm>

#define pi 3.1415926

using namespace std;

void f(int m){

    double t;

    int res;

    t=(m*log(m)-m+0.5*log(2*m*pi))/log(10);

    res=(int)t+1;

    cout<<res<<endl;

}

int main(){

    int n;

    cin>>n;

    int m;

    while(n--){

        cin>>m;

        f(m);

    }

    return 0;

}

P3

大数运算

嗯。。。。

直接上模板

///两种类型的大数运算

///第一种,使用string数组

///第二种,使用int数组,关键都在于对手算的模拟

 

///P1:本人的

/*#include<cstdio>

#include<iostream>

#define LL long long

#define N 10001

using namespace std;

string a[N];

string bigadd(string &num, string add) {

    int goBit = 0;

    if (num.length() < add.length()) {

 

        string tmp = num;

        num = add;

        add = tmp;

    }

    string tmp (num.length() - add.length(), '0');

    add = tmp + add;

    int len1 = num.length(), len2 = add.length();

    for (int i = len1 -1 ; i>= 0; --i) {

 

        int tmp=((num[i]-'0')+(add[i] - '0') + goBit) ;

 

        num[i]=(tmp%100)+'0';

 

        goBit = tmp/100;

    }

    if (goBit != 0)

        num.insert(0, string(1, (char)goBit +'0'));

    return num;

}

string bignumadd(string b,string c,string d,string e){

    string tmp=bigadd(b,c);

    tmp=bigadd(tmp,d);

    if(tmp.length()<e.length()){

        string ans=tmp;

        tmp=e;

        e=ans;

    }

    tmp=bigadd(e,tmp);

    return tmp;

}

void init(){

    a[1]='1';a[2]='1';a[3]='1';a[4]='1';

    for(int i=5;i<=10000;i++){

        a[i]=bignumadd(a[i-1],a[i-2],a[i-3],a[i-4]);

    }

}

int main(){

    int n;

    init();

    while(scanf("%d",&n)!=EOF){

        cout<<a[n];

        cout<<endl;

    }

    return 0;

}*/

///P2:int数组,但编译器无法编译

#include<cstdio>

#include<iostream>

#include<cstring>

using namespace std;

int a[10000][650];

int main(){

    int p=1;

    memset(a,0,sizeof(a));

    a[1][0]=1;

    a[2][0]=1;

    a[3][0]=1;

    a[4][0]=1;

    for(int i=5;i<10000;i++){

        for(int j=0;j<=p;j++)

            a[i][j]=a[i-1][j]+a[i-2][j]+a[i-3][j]+a[i-4][j];

        for(int j=0;j<=p;j++){

            a[i][j+1]+=a[i][j]/10000;

            a[i][j]=a[i][j]%10000;

        }

        if(a[i][p])

            p++;

    }

    int n;

    while(scanf("%d",n)!=EOF){

        int i;

        for(i=p;i>=0;i--)

            if(a[n][i]!=0)

                break;

        printf("%d",a[n][i--]);

        for(;i>=0;i--){

            printf("%04d",a[n][i]);

        }

        cout<<endl;

    }

    return 0;

}

///P3:可编译的int数组类

#include <stdio.h>

#include <string.h>

 

/*自定义的大数表示方法

结构体数组中的每一个int表示一个不超过 10 0000 0000 的数,

它是小于有符号int的最大值的,也就是说把一个很大的数拆分成

多个部分表示,就像我们在做两个数相加时,一位一位相加一样,

我只是用程序9位9位的加,取代人工的1位加法

*/

 

//要求是不超过1000位Fibonacci数,我这里就取MAX_BIG位,最多能表示

//的Fibonacci位数就是 MAX_BIG*9 = 120*9 = 1080位

//我这里用BIG_INTEGER[0]表示最低位

#define MAX_BIG     120

typedef struct{

    //+1:可能产生最高位进位,但我不管,直接丢弃(不然VS2012会报栈被破坏,

    //栈确实会被破坏,VS2012挻智能的)

    unsigned int big[MAX_BIG+1];

}BIG_INTEGER;

 

#define BIG_BASE        1000000000         //进制,不知道不要乱改

/*******************************************************

函数:calc@12

功能:a+b -> c, 大数加法

参数:a,b - 加数; c:和

返回:(无)

*******************************************************/

void calc(BIG_INTEGER* a, BIG_INTEGER* b,BIG_INTEGER* c)

{

    unsigned int sum=0;

    int i=0;

 

    /**************************

    以下循环计算一次加法:

        加数1:abcd

        加数2:efgh

        结果:(a+e+x)(b+f+x)(c+g+x)(d+h+x)

    其中:

        abcd,efgh在这里各代表一个9位数

        x代表来自低位的进位

    ***************************/

    memset(c,0,sizeof(BIG_INTEGER));

    for(i=0; i<MAX_BIG; i++){

        c->big[i] = a->big[i] + b->big[i] + c->big[i];  //做1次9位加,记得加上本身

        c->big[i+1] = c->big[i]/BIG_BASE;               //高位加上结果的进位

        c->big[i] = c->big[i] % BIG_BASE;               //当前位只保留余数

    }

}

 

/************************************************************

函数:fibonacci@4

功能:计算第n位的菲波那契数值

参数:n:第n位, 最低为1

************************************************************/

void fibonacci(int n)

{

    BIG_INTEGER a = {0};

    BIG_INTEGER b = {0};

    BIG_INTEGER c = {0};

    int i=0;

 

    if(n<=1){

        printf("%d\n",n==1);

        return;

    }

 

    //Fibonacci:1,1,2,3,5,8,...

    //为保证第一次计算(n=2)时的结果,所以初值为:a=0,b=1 => c=1

    a.big[0] = 0;

    b.big[0] = 1;

 

    /**************************

    以下循环计算类似小数的Fibonacci:

        f3 = f1 + f2;

        f1 = f2;

        f2 = f3;

    ***************************/

    for(i=2; i<=n; i++){

        calc(&a,&b,&c);

        memcpy(&a,&b,sizeof(BIG_INTEGER));

        memcpy(&b,&c,sizeof(BIG_INTEGER));

    }

    //该for循环忽略输出从最高位开始的为0的一个数值位(9位)

    //当然, 条件 i>0 是必须的,你知道为什么吗?

    for(i=MAX_BIG-1; c.big[i]==0 && i>0; i--)

        ;

    //对于最高位:不需要输出前导的'0'

    printf("%u", c.big[i--]);

    //然后按9位分解输出每个数值位,这个一定要记得输出前导0,

    //一开始我没注意到,就出了问题

    while(i>=0){

        printf("%09u",c.big[i]);

        i--;

    }

    printf("\n");

}

 

int main(void)

{

    int n;

    scanf("%d",&n);

    fibonacci(n);

    return 0;

}

剩下题目卡特兰数前面已总结,DP留到第三章

0 0
原创粉丝点击