小白都要会的基本操作


最大公约数与最小公倍数

int gcd(int a,int b)
{
    if(b==0)  
        return a;
    return gcd(b,a%b);
}

利用STL中的函数

#include<stdio.h>
#include<algorithm>
using namespace std;
int main()
{
    int a,b;
    scanf("%d %d",&a,&b);
    printf("%d",__gcd(a,b));
    return 0;
}

注意是两道下划线。

快速乘与快速幂

int fast_multiply(int a,int b,int mod)
{
     int ans=0;
     while(b!=0)
     {
     if(b%2==1)
          ans=(ans%mod+a%mod)%mod;
     a=(a%mod+a%mod)%mod;
     b/=2;
     }
      return ans;
}
int fast_pow(int a,int b,int mod)//快速幂
{
    int ans=1;//最终求余结果
    while(b)
    {
        if(b%2==1)//b的末位为1,也可表示为 b&1
            ans=(ans%mod+a%mod)%mod;
        a=(a%mod*a%mod)%mod;
        b/=2;//n往右移一位,也可表示为n>>=1
    }
    return ans%mod;
}

二分查找

①算法原理(以升序进行二分查找)

a.如果待查序列为空,那么返回-1,并退出算法,表示查找不到目标元素;

b.如果待查序列不为空,则将它的中间元素与要查找的目标元素进行匹配,检查它们是否相等;

c.如果相等,则返回该中间元素的索引,并退出算法;此时查找成功;

d.如果不相等,再比较这两个元素的大小;

e.如果该中间元素大于目标元素,那么就将当前序列的前半部分作为新的待查序列(此时后半部分的所有元素都大于目标元素);

f.如果该中间元素小于目标元素,那么就将当前序列的后半部分作为新的待查序列(此时前半部分的所有元素都大于目标元素)

g.在新的待查序列重新开始第a步的工作;

int BinnarySearch(int arr[],int len,int target)//二分查找(折半查找)
{
    //定义并初始化left和right
    int left=0;
    int right=len-1;
    //如果left小于等于right,则说明待查序列不为空,需将其中间元素和目标元素进行匹配比较
    while(left<=right)
    {
        //计算中间元素的索引
        int mid=(left+right)/2;
        if(arr[mid]==target)
            return mid;
        //查找成功,返回该中间元素的索引
        else if(arr[mid]<target)
            left=mid+1;//对后半部分作为新的待查序列
        else if(arr[mid]>target)
            right=mid-1;//对前半部分作为新的待查序列
    }
    //没有匹配的元素
    return -1;
}
/********************************************************************************/
//此有序数列为降序数组
int BinnarySearch(int arr[],int len,int target)//二分查找(折半查找)
{
    //定义并初始化left和right
    int left=0;
    int right=len-1;
    //如果left小于等于right,则说明待查序列不为空,需将其中间元素和目标元素进行匹配比较
    while(left<=right)
    {
        //计算中间元素的索引
        int mid=(left+right)/2;
        if(arr[mid]==target)
            return mid;
        //查找成功,返回该中间元素的索引
        else if(arr[mid]>target)//上下两种变化在此处
            left=mid+1;//对后半部分作为新的待查序列
        else if(arr[mid]<target)//上下两种变化在此处
            right=mid-1;//对前半部分作为新的待查序列
    }
    //没有匹配的元素
    return -1;
}

注意:while循环条件是<=而不是<.

素数打表

常规思路:

int prime(int n)
{
    int i;
    for(i=2;i*i<=n;i++)
    {
        if((n%i)==0)
            return 0;
    }
    return 1;
}

埃氏筛法

int prime[n]={0},p[n+1]={0},i,j,count=0;
prime[1]=1;
for(i=2;i<=n;i++)
{
    if(prime[i]==0)
    {
        count++;记录素数个数
        p[count]=i;存储素数
    }
    for(j=i+i;j<=n;j+=i)
        prime[j]=1;剔除倍数
 }

欧拉筛法

/欧拉筛法
#include <stdio.h>
#define N 1000000
int main()
{
    long long int a[N]= {0},x,y,i,j,k;//数组a初始化为0
    for(i=2; i<N; i++)
        if(!a[i])//a[i]==0就说明i是素数
            for(j=i*i; j<N; j+=i)
                a[j]=1;//将i的倍数标记为1;
    for(i=2; i<N; i++)
        if(a[i]==0)
           printf("%lld\n",i);//输出素数
    return 0;
}

前缀和与差分

(1)前缀和

假设有数组a为[1, 2, 3, 4, 5]那么代表它的前缀和数组的数组b就是1, 3, 6, 10, 15,也就是说,a[n] = b[1] + b[2] + … + b[n - 1] + b[n],这里我们假设数组是从下标1开始的,这样我们可以减去一步繁琐的判断

通过前缀和,我们可以快速的求出原数组从l到r的区间中元素的和,等于S[l ~ r] = b[r] - b[l - 1]

#include <iostream>
using namespace std;
const int N = 1e5 + 1;
int n, m;
int a[N], s[N];
int main() 
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
    for (int i = 1; i <= n; ++i) s[i]= s[i - 1] + a[i];
    while (m--) 
    {
        int l, r;
        scanf("%d%d", &l, &r);
        printf("%d\n", s[r] - s[l - 1]);
    }
}

差分

差分就是前缀和的逆运算,a是b的差分数组,b就是a的前缀和数组,通过差分,我们可以快速实现对一个区间内所有元素的同增同减操作。
假设我们让a[2]到a[6]的元素全部加1,只需让b[2]+=1,b[7]-=1;
具体代码如下:

#include <iostream>
using namespace std;
const int N = 100010;
int a[N], b[N];
int n, m;
int main() 
{
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) 
        scanf("%d", &a[i]);
    for (int i = 1; i <= n; i++)
         b[i] = a[i] - a[i - 1];
    while (m--) 
    {
        int l, r, c;
        scanf("%d%d%d", &l, &r, &c);
        b[l] += c;
        b[r + 1] -= c;
    }
    for (int i = 1; i <= n; ++i) 
    {
        a[i] = a[i - 1] + b[i];
        printf("%d ", a[i]);
    }
}

搜索

棋盘问题(应用DFS)
在这里插入图片描述
代码如下:

#include<stdio.h>
#include<string.h>
//#表示棋盘区域,.表示空白区域
int flag[20],n,k;//n表示矩阵宽度,k表示放置棋的个数,flag[9]表示标志每一列是否被使用
char a[9][9];//存放棋盘
long long sum;//表示成立的方案数
void search(int x,int y)//搜索(y表示放置棋子数)
{
    int i,j;
    if(y==0)//棋子全用完,sum++
    {
        sum++;
        return ;
    }
    if(x==n)
        return ;
    for(i=x;i<=n-1;i++)//寻找棋子放在哪一行
        for(j=0;j<=n-1;j++)//寻找棋子放在哪一列
        {
            if(a[i][j]=='.'||flag[j]==1)//该位置行列已被放置或该位置不能放置
                continue;
            flag[j]=1;
            //printf("%lld\n",sum);
            search(i+1,y-1);//找到一个棋子,去下一行寻找
            flag[j]=0;//回溯
        }
}
int main()
{
    int i,j;
    while(scanf("%d%d",&n,&k)!=EOF)
    {
        if(n==-1&&k==-1)
            break;
        memset(a,0,sizeof(a));//将棋盘清0
        memset(flag,0,sizeof(flag));//将所有标记列位清0
        sum=0;
        for(i=0;i<=n-1;i++)
            scanf("%s",a[i]);
        search(0,k);
        printf("%lld\n",sum);
    }
    return 0;
}

示例:地下城主(Dungeon Master(利用队列和BFS搜索))

在这里插入图片描述

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<queue>
using namespace std;
//利用结构体+队列+bfs搜索
//S表示起始位置,E表示退出位置,#表示有岩石(不能走),.表示能走的路径
int l,r,c,flag[31][31][31];//l表示楼层数,r表示每层的行数,c表示每层的列数
//flag标记走过的路程
char s[31][31][31];//存储地牢图
int direction[6][3]={0,1,0,0,-1,0,0,0,1,0,0,-1,1,0,0,-1,0,0};
//移动方向(行右行左列上列下层上层下),三维移动处理
typedef struct{
    int x;
    int y;
    int z;
    int step;//存储步数
}zb;
zb start={0},over={0};//起始位置坐标,结束位置坐标
int bfs(zb a,zb b)//a表示起始,b表示终点
{
    int i;
    queue <zb> q;
    zb m,n;//m表示当前坐标,n表示移动后的坐标
    m=a;
    flag[m.x][m.y][m.z]=1;
    q.push(m);
    while(q.size()>0)
    {
        m=q.front();
        //printf("%d\n",m.step);
        q.pop();
        if(m.x==b.x&&m.y==b.y&&m.z==b.z)//到达终点
            return m.step;
        for(i=0;i<=5;i++)
        {
            n.x=m.x+direction[i][0];
            n.y=m.y+direction[i][1];
            n.z=m.z+direction[i][2];
            if(n.x>l-1||n.x<0||n.y>r-1||n.y<0||n.z>c-1||n.z<0)//越界
                continue;
            if(s[n.x][n.y][n.z]=='#'||flag[n.x][n.y][n.z]==1)//撞墙或已走过
                continue;
            flag[n.x][n.y][n.z]=1;
            n.step=m.step+1;
            q.push(n);
        }
    }
    return 0;
}
int main()
{
    int i,j,k,sum;
    while(scanf("%d%d%d",&l,&r,&c)!=EOF)
    {
        memset(flag,0,sizeof(flag));
        if(l==0&&r==0&&c==0)
            break;
        for(i=0;i<=l-1;i++)
        {
            for(j=0;j<=r-1;j++)
            {
                scanf("%s",s[i][j]);//输入
                for(k=0;k<=c-1;k++)
                {
                    if(s[i][j][k]=='S')//标记起点
                        start.x=i,start.y=j,start.z=k;
                    if(s[i][j][k]=='E')//标记终点
                        over.x=i,over.y=j,over.z=k;
                }
            }
        }
        //for(i=0;i<=l-1;i++)
            //for(j=0;j<=r-1;j++)
                //puts(s[i][j]);
        sum=bfs(start,over);//bfs搜索
        if(sum==0)
            printf("Trapped!\n");
        else
            printf("Escaped in %d minute(s).\n",sum);
    }
    return 0;
}

⑤迷宫问题(利用队列和BFS搜索)
在这里插入图片描述

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<queue>
using namespace std;
//利用bfs搜索+队列+结构体
//0表示能走的路径,1表示墙壁不能过
int a[5][5],b[5][5],c[5][5],flag[5][5];//a存储迷宫图,flag标记走过的路
//b,c分别记录到达这个点的上一次的x和y坐标
int direction[4][2]={0,1,0,-1,1,0,-1,0};//移动方向(上下右左)
typedef struct{
    int x;
    int y;
}zb;
queue <zb> q;
void bfs()
{
    int i,m,n;//m,n存储移动后的横纵坐标(x,y)
    zb s={0},t;
    q.push(s);
    while(q.size()>0)//队列不为空,到达终点时return;
    {
        s=q.front();
        q.pop();
        if(s.x==4&&s.y==4)//到达终点
            return ;
        for(i=0;i<=3;i++)//方向移动
        {
            m=s.x+direction[i][0];
            n=s.y+direction[i][1];
            if(m<0||n<0||m>4||n>4||flag[m][n]==1||a[m][n]==1)
                continue;
            t.x=m;//存储移动后的横坐标(x)
            t.y=n;//存储移动后的纵坐标(y)
            q.push(t);//存进队列中
            flag[m][n]=1;//对走过的路径标记1
            b[m][n]=s.x;//存储上一次走的横坐标
            c[m][n]=s.y;//存储上一次走的纵坐标
        }
    }
}
void f1(int x,int y)//借助递归输出
{
    if(x!=0||y!=0)
        f1(b[x][y],c[x][y]);
    printf("(%d, %d)\n",x,y);
}
int main()
{
    int i,j;
    for(i=0;i<=4;i++)
        for(j=0;j<=4;j++)
            scanf("%d",&a[i][j]);//输入
    bfs();//搜索
    f1(4,4);//输出
    return 0;
}

⑦非常可乐(利用队列和BFS搜索)在这里插入图片描述
代码如下
题解:模拟倒水(三个杯子与之类似)——转换成两杯子间的倒水情况共6种(详情见代码)
在这里插入图片描述

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
//结构体+队列+bfs搜索
//倒水情况分为如下6种:(s向nm倒,n向sm倒,m向sn倒)
//s→n,s→m
//n→s,n→m
//m→s,m→n
int s,n,m,flag[102][102][102],sum;//s表示可乐的体积,n,m分别表示两个杯子的容量,flag标记是否被倒过,sum表示倒的最少次数
typedef struct{
    int x;//存储s
    int y;//存储n
    int z;//存储m
    int step;//存储倒的次数

}zb;
void bfs()
{
    int c;
    zb a={0},b={0};//a表示当前,b表示倒水后
    queue <zb> q;
    a.x=s;
    flag[a.x][a.y][a.z]=1;
    q.push(a);
    while(q.size()>0)
    {
        a=q.front();
        q.pop();
        if((a.x==0&&a.y==a.z)||(a.y==0&&a.x==a.z)||(a.z==0&&a.x==a.y))//平分且两杯子有另一杯子为空
        {
            sum=a.step;
            return ;
        }
        if(a.x>0&&a.y!=n)//s倒n
        {
            c=n-a.y;
            if(c<a.x)//n倒满
                b.x=a.x-c,b.y=n;
            else b.x=0,b.y=a.y+a.x;//s倒光
            b.z=a.z;
            b.step=a.step+1;
            if(flag[b.x][b.y][b.z]==0)
            {
                flag[b.x][b.y][b.z]=1;
                q.push(b);
            }
        }
        if(a.x>0&&a.z!=m)//s倒入m
        {
            c=m-a.z;
            if(c<a.x)//m倒满
                b.x=a.x-c,b.z=m;
            else b.x=0,b.z=a.z+a.x;//s倒光
            b.y=a.y;
            b.step=a.step+1;
            if(flag[b.x][b.y][b.z]==0)
            {
                flag[b.x][b.y][b.z]=1;
                q.push(b);
            }
        }
        if(a.y>0&&a.x!=s)//n倒入s
        {
            c=s-a.x;
            if(c<a.y)//s倒满
                b.y=a.y-c,b.x=s;
            else b.y=0,b.x=a.x+a.y;//n倒光
            b.z=a.z;
            b.step=a.step+1;
            if(flag[b.x][b.y][b.z]==0)
            {
                flag[b.x][b.y][b.z]=1;
                q.push(b);
            }
        }
        if(a.y>0&&a.z!=m)//n倒入m
        {
            c=m-a.z;
            if(c<a.y)//m倒满
                b.y=a.y-c,b.z=m;
            else b.y=0,b.z=a.z+a.y;//n倒光
            b.x=a.x;
            b.step=a.step+1;
            if(flag[b.x][b.y][b.z]==0)
            {
                flag[b.x][b.y][b.z]=1;
                q.push(b);
            }
        }
        if(a.z>0&&a.x!=s)//m倒入s
        {
            c=s-a.x;
            if(c<a.z)//s倒满
                b.z=a.z-c,b.x=s;
            else b.z=0,b.x=a.x+a.z;//m倒光
            b.y=a.y;
            b.step=a.step+1;
            if(flag[b.x][b.y][b.z]==0)
            {
                flag[b.x][b.y][b.z]=1;
                q.push(b);
            }
        }
        if(a.z>0&&a.y!=n)//m倒入n
        {
            c=n-a.y;
            if(c<a.z)//n倒满
                b.z=a.z-c,b.y=n;
            else b.z=0,b.y=a.y+a.z;//m倒光
            b.x=a.x;
            b.step=a.step+1;
            if(flag[b.x][b.y][b.z]==0)
            {
                flag[b.x][b.y][b.z]=1;
                q.push(b);
            }
        }
    }
}
int main()
{
    while(scanf("%d%d%d",&s,&n,&m)!=EOF)
    {
        if(s==0&&n==0&&m==0)
            break;
        if(s%2==1)//奇数的情况下不可能平分
            printf("NO\n");
        else//偶数分情况讨论
        {
            memset(flag,0,sizeof(flag));
            sum=0;
            bfs();
            if(sum==0)
                printf("NO\n");
            else printf("%d\n",sum);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值