开始学习ACM的博客【2】——数学基础

数学基础

目录

数学基础

最小公倍数

欧几里得算法

 计算N的N次幂的个位数

循环节

新斐波那契数列

人见人爱A^B

查找整数

二分查找法

三分算法


前言:int(-2147483648~+2147483647)

在写程序时,需要注意数据的范围。如:使用高斯公式时(n*(n+1)/2)当n=50000时,最终结果不会超过int的范围,但计算n*(n+1)时会超过,使得结果出错。

此时,可以使用long long,也可以先对n和n+1进行奇偶的判断,再将其中为偶数的一项先除以2,使数据大小不会超出int的范围。

最小公倍数

给定两个正整数,计算这两个数的最小公倍数。

输入样例

10 14

输出样例

70

法一

枚举

用两数中较大的一个数进行倍数枚举,并判断是否为另一个数的倍数,以此寻找最小公倍数。

法二

数学方法

最小公倍数LCM(a,b)=a*b/最大公约数GCD(a,b)

使用公式时要注意a*b会不会超过int的范围,故建议写成 a/GCD(a,b)*b

问题变为求最大公约数GCD

欧几里得算法

辗转相除法:辗转相除法是求两个自然数的最大公约数的一种方法,也叫欧几里德算法

用辗转相除法求几个数的最大公约数,可以先求出其中任意两个数的最大公约数,再求这个最大公约数与第三个数的最大公约数,依次求下去,直到最后一个数为止。最后所得的那个最大公约数,就是所有这些数的最大公约数。

例如,求(319,377):

∵ 319÷377=0(余319)

∴(319,377)=(377,319);

∵ 377÷319=1(余58)

∴(377,319)=(319,58);

∵ 319÷58=5(余29)

∴ (319,58)=(58,29);

∵ 58÷29=2(余0)

∴ (58,29)= 29;

∴ (319,377)=29

int gcd(int big,int small)
{
  int temp;
  while(small!=0)
  {
    temp=big%small;
    big=small;
    small=temp;
  }
  return big;
}
//注意,这个代码中输入时大小数据颠倒不会出错(经过一个循环后会被更正)

最终代码

#include<stdio.h>
int gcd(int big,int small);
int main()
{
    int a,b;
    while(scanf("%d %d",&a,&b)==2)
    {
        int lcm;
        lcm=a/gcd(a,b)*b;
        printf("%d\n",lcm);
    }
    return 0;
}
int gcd(int big,int small)
{
    int temp;
    while(small!=0)
    {
        temp=big%small;
        big=small;
        small=temp;
    }
    return big;
}

 计算N的N次幂的个位数

给定一个正整数N,请计算并输出N的N次方的个位数。

输入第一行是一个正整数T,表示有T组测试用例。

接下来的T行,每行包含一个正整数N(1<=N<=1,000,000,000)。                     
每组数据都输出N的N次幂的个位数,每组数据输出一行。

输入样例

2

3

4

输出样例

7

6

 法一

直接计算N*N数据太大,要每乘一次N就取一次个位。(如果N的数据特别大,如本题中,则会超时)

法二

循环节

一个循环的单位,常用于大数据的有规律变换中

先取出N的个位数,再找出其的循环节(循环节的长度一定不超过5个数(奇\偶)),再求即可。

#include<stdio.h>
int gewei(int n);
int MyPow(int a,int b);

int main()
{
    int num;
    scanf("%d",&num);
    for(int i=0;i<num;i++)
    {
        int n;
        int a;
        int cycle[5]={0};//创建个位的循环数组
        int sn=1;//有效的循环个数
        scanf("%d",&n);
        a=gewei(n);
        cycle[0]=a;
        for(int j=1;j<5;j++)
        {
            cycle[j]=gewei(MyPow(a,j+1));
            if(cycle[0]==cycle[j])
            {
                sn=j;
                break;
            }
        }
        int x;//判断是否取循环节中的哪一位
        if(n%sn==0)
        {
            x=sn-1;
        }
        else
        {
            x=n%sn-1;
        }
        printf("%d\n",cycle[x]);
    }
    return 0;
}

int gewei(int n)//取个位
{
    return (n%10);
}

int MyPow(int a,int b)//取整数的次方
{
    int final=1;
    for(int i=0;i<b;i++)
    {
        final*=a;
    }
    return final;
}

新斐波那契数列

现在,有一个新的斐波那契数列,定义如下:

F(0) = 7,
F(1) = 11,
F(n) = F(n-1) + F(n-2) (n>=2).

Input
输入包含多组测试样例,每组测试样例包含一个整数n(n < 1,000,000).

Output
如果F(n)能够被3整除,请输出”yes”,否则请输出”no”。

输入样例

0

1

2

输出样例

no

no

yes

此题若求出F(n)一定会超出范围,故转为求F(n-1)和F(n-2) 模3的值,后可以发现F(1)、F(2)……模3后的余数呈循环节变换,以此找到规律。

#include<stdio.h>
#define MAX 20

int judge(int n);

int main()
{
    int n;
    while(scanf("%d",&n)==1)
    {
        if(judge(n)==1)//判断输出
        printf("yes\n");
        else
        printf("no\n");

    }
    return 0;
}

int judge(int n)//判断是否能被3整除
{
    n+=1;//调整脚标
    int a[MAX]={0};
    int sn;//循环节个数
    a[0]=7%3;
    a[1]=11%3;
    a[2]=(a[0]+a[1])%3;
    for(int i=2;i<MAX;i++)//计算循环节
    {
        a[i+1]=(a[i-1]+a[i])%3;
        if(a[i]==a[0]&&a[i+1]==a[1])
        {
            sn=i;
            break;
        }
    }
    if(n%sn==0)
    {
        if(a[sn-1]==0)
        return 1;
        else
        return 0;
    }
    else
    {
        if(a[n%sn-1]==0)
        return 1;
        else
        return 0;
    }

}

人见人爱A^B

求A^B的最后三位数表示的整数。
说明:A^B的含义是“A的B次方”

Input
输入数据包含多个测试实例,每个实例占一行,由两个正整数A和B组成(1<=A,B<=10000),如果A=0, B=0,则表示输入数据的结束,不做处理。

Output
对于每个测试实例,请输出A^B的最后三位表示的整数,每个输出占一行。

输入样例

2 3

12 6

0 0

输出样例

8

984

此题应学会快速计算幂的方法。

法一

递归

int power(int a,int n)
{
  int ans;
  if(n==0) ans=1;
  else
  {
    ans=power(a*a,n/2);//底数平方,指数减半
    if(n%2==1) ans*=a; //考虑到指数为奇数的情况
  }
  return ans;
}

法二

循环

int power(int a,int n)
{
  int ans=1;
  while(n)
  {
    if(n%2==1) ans*=a; //奇数情况
    a=a*a;             //底数平方
    n=n/2;             //指数减半
  }
  return ans;
}

最终解法

#include<stdio.h>
int main()
{
    int a,b;
    while(scanf("%d %d",&a,&b)==2)
    {
        if(a==0&&b==0)
        {break;}
        else
        {
            int ans=1;
            a=a%1000;
            while(b)
            {
                a=a%1000;
                if(b%2==1) ans*=a;
                a*=a;
                b/=2;
                ans=ans%1000;
            }
            printf("%d\n",ans);
        }
    }
}

查找整数

在一组数据中(递增)找到是否存在某个数

二分查找法

前提:数据是单调排序

递归

int BiSearch(int a[],int x,int left,int right)
{
  if(left>right) //此处不可有=号
    return -1;
  else
  {
    int mid=(left+right)/2;
    if(a[mid]==x) 
      return mid;
    else if(x>a[mid])
      return BiSearch(a,x,mid+1,right);
    else if(x<a[mid])
      return BiSearch(a,x,left,mid-1);
  }
}

循环

int BiSearch(int a[],int n,int x)
{
  int left=0,right=n-1;
  while(left<=right) //注意此处=不能少
  {
    int mid=(left+right)/2; //整数除法
    if(a[mid]==x)           //找到时
      reutrn mid;
    if(x>a[mid])            //比中值大
      left=mid+1;
    else                    //比中值小
      right=mid-1;
  }
  return -1;
}

二分法的查找时间增加是对数式的增加

例题:

题目描述
给定方程 8x^4 + 7x^3 + 2x^2 + 3x + 6 == Y,请计算x在[0,100]范围内的解。

Input
输入数据首先是一个正整数T(1<=T<=100),表示有T组测试数据。
接下来T行,每行包含一个实数Y ( fabs(Y) <= 1e10 )。

Output
请计算并输出方程在范围[0,100]内的解,结果精确到小数点后4位。
如果无解,则请输出“No solution!”

输入样例

2

100

-4

输出样例

1.6152
No solution!

使用二分法不断地趋近正确值。

#include<stdio.h>
#include<math.h>

double F(double x);

int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
    {
        double y;
        scanf("%lf",&y);
        if(F(0)<=y&&F(100)>=y)
        {
            double left=0;
            double right=100;
            double mid=50;
            while(right-left>1e-6)//确定精度
            {
                mid=(left+right)/2;
                if(F(mid)>y)
                {
                    right=mid-1e-7;//调整精度
                }
                else
                {
                    left=mid+1e-7;
                }
            }
            printf("%0.4f\n",mid);

        }
        else{
            printf("No solution!\n");
        }

    }
}

double F(double x)
{
    double y;
    y=8*pow(x,4)+7*pow(x,3)+2*pow(x,2)+3*x+6;
    return y;
}

三分算法

若是有凸型的一组数据找极大/极小值,可使用三分法(此处的函数不需要可导)

mid1=(left*2+right)/3
mid2=(left+right*2)/3

此时,有最左值、mid1、mid2、最右值,将函数分为三段,比较mid1和mid2,若mid1>mid2,则说明极大值一定不在最右边的一段;若mid1<mid2,则说明极大值一定不在最左边的一段;若mid1=mid2,则说明极大值在中间的一段。以此种方法不断减少范围,确定最终值。

 【算法与数据结构】三分查找

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值