最大公约数与最小公倍数
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;
}
#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;
}