蓝桥杯赛前整理

蓝桥杯赛前整理

离比赛还不到一周,刷题停了,回顾下之前经常遇到的点

比赛里可以用万能头文件:#include <bits/stdc++.h>
由于比赛用的是Windows评测机,输出longlong时建议用%i64d

1、模运算
关于模运算的几个公式:
在这里插入图片描述
第4个公式,如果b较大时,可以采用费马小定理化简
比如上周日的一道模拟题:
在这里插入图片描述
可以先用费马小定理对n进行化简,令n=n%100,最后利用快速幂就能算出答案
费马小定理:a^(p-1)%p=1 条件:p为质数并且a<p
代码:

#include <bits/stdc++.h>

using namespace std;

const int mod=101;

int fast_pow(int a,int b,int p)//快速幂取模
{
    int r=1%p;
    a%=p;
    while(b)
    {
        if(b&1)
        {
            r=(r*a)%p;
        }
        b>>=1;
        a=(a*a)%p;
    }
    return r;
}
int main()
{
    int n=1;
    int sum=0;
    for(int i=1;i<=2019;i++)
    {
        n=n*2019%100;//不断将n进行化简
    }
    for(int i=1;i<=11;i++)
    {
        sum+=fast_pow(i,n,mod);
    }
    cout<<sum%mod<<endl;
    return 0;
}

2、最大公约数
代码实现:

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

PS:也可以使用c++库里的__gcd()函数,但是要注意前面是两条下划线

3、拆数问题
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
const int n=20192019;
int ans;
int main()
{
    for(int i=1;i<=n;i++)
    {
        int x=i;
        while(x)
        {
            if(x%10==7)
            {
                ans++;
            }
            x/=10;
        }
    }
    printf("%d\n",ans);
    return 0;
}

4、判断是否为素数以及筛法求素数
注意:1不是素数!

判断是否为素数:

bool is_PrimNum(int n)
{
    if(n==1)
    {
        return false;
    }
    if(n==2)
    {
        return true;
    }
    for(int i=2;i*i<=n;i++)
    {
        if(n%i==0)
        {
            return false;
        }
    }
    return true;
}

筛法求素数,以下面这道题为例:
在这里插入图片描述
AC代码:

#include <bits/stdc++.h>
using namespace std;
bool p[1000005];
int main()
{
    int m;
    scanf("%d", &m);
    //预处理 假设都为质数
    for (int i = 2; i <= 1000000; i++)
	{
        p[i] = true;
    }
    //筛法求素数 欧式筛
    for (int i = 2; i * i <= 1000000; i++)
	{
        if (p[i])
		{
            for (int j = 2; i * j <= 1000000; j++)
			{
                p[i*j] = false;
            }
        }
    }
    while (m--)
	{
        int n;
        scanf("%d", &n);
        for (int i = 2;i < n; i++)
		{
            if (p[i] && p[n - i])
			{
                printf("%d %d\n", i, n - i);
                break;
            }
        }
    }
    return 0;
}

5、日期问题
注意判断是否为闰年,一般日期问题就是对翻日历过程的模拟
以高斯的日记为例:
在这里插入图片描述
AC代码:

#include <bits/stdc++.h>
using namespace std;
int is_leap_year(int y)//闰年判断
{
	if(y%100!=0&&y%4==0||y%400==0)
	{
		return 1;
	}
	else
	{
		return 0;
	}
}
int main()
{
	int y=1777;
	int m=4;
	int d=30;
	int k;
	cin>>k;
	int day[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
	//模拟翻日历过程
	for(int i=2;i<=k;i++)//由于出生那天就算一天 所以循环从2开始
	{
		if(is_leap_year(y))
		{
			day[2]=29;
		}
		else
		{
			day[2]=28;
		}
		d++;
		if(d>day[m])
		{
			d=1;
			m++;
		}
		if(m>12)
		{
			m=1;
			y++;
		}

	}
	printf("%04d-%02d-%02d",y,m,d);
	return 0;
}

6、二分
①两个二分函数:

upper_bound();//找出大于目标元素的第一个元素

lower_bound();//找出等于或者大于目标元素的第一个元素

②两个二分模板:

//求最大值最小 区间为[l,mid] [mid+1,r]
while(l<r)
{
    int mid=(l+r)>>1;
    if(check(mid))
    {
        r=mid;
    }
    else
    {
        l=mid+1;
    }
}
//求最小值最大 区间为[l,mid-1] [mid,r]
while(l<r)
{
    int mid=(l+r+1)>>1;
    if(check(mid))
    {
        l=mid;
    }
    else
    {
        r=mid-1;
    }
}

7、DFS和BFS
①数独问题:(DFS)
在这里插入图片描述
AC代码:

#include <bits/stdc++.h>
using namespace std;
char s[15][15];
bool f;
bool vx[10][10];
bool vy[10][10];
bool vv[10][10];
void dfs(int x,int y)
{
    if(f)
    {
        return;
    }
    if(x==9)
    {
        f=true;
        for(int i=0;i<9;i++)
        {
            for(int j=0;j<9;j++)
            {
                if(j!=8)//判断行末空格
                {
                    printf("%c ",s[i][j]);
                }
                else
                {
                    printf("%c\n",s[i][j]);
                }
            }
        }
        return ;
    }
    if(y==9)
    {
        dfs(x+1,0);
        return ;
    }
    if(s[x][y]!='*')
    {
        dfs(x,y+1);
        return ;
    }
    for(int i=1;i<=9;i++)
    {
        if(!vx[x][i]&&!vy[y][i]&&!vv[x/3*3+y/3][i])
        {
            s[x][y]='0'+i;
            vx[x][i]=true;
            vy[y][i]=true;
            vv[x/3*3+y/3][i]=true;
            dfs(x,y+1);
            vx[x][i]=false;
            vy[y][i]=false;
            vv[x/3*3+y/3][i]=false;
            s[x][y]='*';
        }
    }
}
int main()
{
    for(int i=0;i<9;i++)
    {
        for(int j=0;j<9;j++)
        {
            scanf(" %c",&s[i][j]);
        }
    }
    for(int i=0;i<9;i++)
    {
        for(int j=0;j<9;j++)
        {
            if(s[i][j]!='*')
            {
                vx[i][s[i][j]-'0']=true;
                vy[j][s[i][j]-'0']=true;
                vv[i/3*3+j/3][s[i][j]-'0']=true;
            }
        }
    }
    dfs(0,0);
    return 0;
}

②密码锁(BFS)
BFS通常与结构体一起使用,并且在结构体中,要注意构造函数,还要注意BFS的要在入队之前标记
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
bool vis[11][11][11][11];//分别标记每一位
struct node
{
	int num[4];
	int step;
}first,last;
queue<node> q;
void bfs()
{
	node a,next;
	a=first;
	a.step=0;
	q.push(a);
	memset(vis,0,sizeof(vis));
	vis[a.num[0]][a.num[1]][a.num[2]][a.num[3]]=true;
	while(!q.empty())
    {
        a=q.front();
        q.pop();
        if(a.num[0]==last.num[0]&&a.num[1]==last.num[1]&&a.num[2]==last.num[2]&&a.num[3]==last.num[3])
        {
            printf("%d\n",a.step);
            return ;
        }
        for(int i=0;i<4;i++)
        {
            next=a;
            next.num[i]++;
            if(next.num[i]==10)
            {
                next.num[i]=1;
            }
            if(!vis[next.num[0]][next.num[1]][next.num[2]][next.num[3]])
            {
                vis[next.num[0]][next.num[1]][next.num[2]][next.num[3]]=true;
                next.step++;
                q.push(next);
            }
        }
        for(int i=0;i<4;i++)
        {
            next=a;
            next.num[i]--;
            if(next.num[i]==0)
            {
                next.num[i]=9;
            }
            if(!vis[next.num[0]][next.num[1]][next.num[2]][next.num[3]])
            {
                vis[next.num[0]][next.num[1]][next.num[2]][next.num[3]]=true;
                next.step++;
                q.push(next);
            }
        }
        for(int i=0;i<3;i++)
        {
            next=a;
           swap(next.num[i+1],next.num[i]);
            if(!vis[next.num[0]][next.num[1]][next.num[2]][next.num[3]])
            {
                vis[next.num[0]][next.num[1]][next.num[2]][next.num[3]]=true;
                next.step++;
                q.push(next);
            }
        }
    }
}
int main()
{
	char s1[10];
	char s2[10];
	scanf("%s%s",s1,s2);
	for(int i=0;i<4;i++)
	{
		first.num[i]=s1[i]-'0';
		last.num[i]=s2[i]-'0';
	}
	bfs();

	return 0;
}

注意:当数据范围较大或者求最值问题时,选用BFS,当要求输出所有情况时,选用DFS。

8、vector、map、set
①当要求数组较大时,利用vector,但是要注意二维vector的使用,这里将二维vector比喻成书包和钱包,也就是说,当书包里有钱包时,我们才能往钱包里面放钱,钱包就相当于一维的vector,而书包就相当于二维的vector。vector常用于构造一张无向图。
②map常用于记录某个元素出现了多少次
③set常用于去重排序
注意:map和set的遍历需要迭代器(iterator)。三个容器的头元素都是begin,尾元素为end

9、几个常用的函数
①sort();//排序函数 默认从小到大排列 也可以增加一个函数让它从大到小排列
②find();//string类中的查找函数 找到返回下标 没找到返回nops
③fgets();//不会读入空格 但是会读入回车 一般读一个数组时使用 一般写成:fgets(s,n-1,stdin);//n为字符数组的个数
④next_permutation();//全排列函数 配合do—while使用
⑤getline();//读入一个string 当遇到回车时结束 一般写成:getline(cin,str);//str为string类型

10、01背包

#include <bits/stdc++.h>
using namespace std;
int dp[1010];
int w[21],c[21];
int main()
{
  	int N,V;
    cin>>N>>V;
    for(int i=1;i<=N;i++)
    {
        cin>>w[i]>>c[i];
    }
    for(int i=1;i<=N;i++)
    {
        for(int j=V;j>=c[i];j--)
        {
            dp[j]=max(dp[j-c[i]]+w[i],dp[j]);
        }
    }
    cout<<dp[V]<<endl;
    return 0;
}

蒜头君回家:(注意边界情况的判断)
在这里插入图片描述

#include <iostream>
#include <algorithm>
using namespace std;
int a[110][110];
int dp[110][110];
int main() 
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i++) 
    {
        for (int j = 1; j <= n; j++) 
        {
            cin >> a[i][j];
        }
    }
    dp[1][1]=0;//一开始时 体力为0
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(i==1&&j==1)
            {
                continue;
            }
            else if(i==1)//当为第一行时 只能从第一列转移过来
            {
                dp[i][j]=dp[i][j-1]+a[i][j];
            }
            else if(j==1)//当为第一列时 只能从第一行转移过来
            {
                dp[i][j]=dp[i-1][j]+a[i][j];
            }
            else
            {
                dp[i][j]=min(dp[i-1][j],dp[i][j-1])+a[i][j];
            }
        }
    }
    cout<<dp[n][n]<<endl;
    return 0;
}

11、动态规划的几个模型
①最长上升子序列

//子序列 可以是不连续的
#include <bits/stdc++.h>
using namespace std;
const int N=100000;
int dp[N];
int n;
int a[N];
int ans;
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	for(int i=1;i<=n;i++)
	{
		dp[i]=1;//初始化时为1 当没有时 自己就是一个子序列
		for(int j=1;j<=i;j++)
		{
			if(a[j]<a[i])
			{
				dp[i]=max(dp[i],dp[j]+1);//把第i项接到第j项后面
			}
		}
		ans=max(ans,dp[i]);//可以用ans保存最大值 也可以循环一遍dp数组求出最大值
	}
	cout<<ans<<endl;
	return 0;
}

②最长公共子序列

#include <iostream>
#include <cstring>
#include <string>
using namespace std;
int dp[110][110];

int main()
{
	string a,b;
    memset(dp,0,sizeof(dp));
    cin>>a>>b;
    int lena=a.size();
    int lenb=b.size();
    for(int i=1;i<=lena;i++)
    {
        for(int j=1;j<=lenb;j++)
        {
        	if(a[i-1]==b[j-1])
            {
                dp[i][j]=dp[i-1][j-1]+1;
            }
            else
            {
                dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
            }
        }
    }
    cout<<dp[lena][lenb]<<endl;
    return 0;
}

③最大子段和

#include <bits/stdc++.h>
using namespace std;
const int N=10005;

int sum;
int ans=-100000;
int a[N];
int n;
int main()
{
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		ans=max(ans,a[i]);
	}
	if(ans<=0)
	{
		cout<<ans<<endl;
	}
	else
	{
		for(int i=1;i<=n;i++)
		{
			if(sum+a[i]<0)
			{
				sum=0;
			}
			else
			{
				sum+=a[i];
			}
			ans=max(ans,sum);
		}
		//ans=max(ans,sum);
	}
	//ans=max(ans,sum);
	cout<<ans<<endl;
	return 0;
}

④爬楼梯
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
int n;
const int mod=100007;

long long dp[200005];
long long ans;
int main()
{
    scanf("%d",&n);
    dp[0]=dp[1]=1;
    for(int i=2;i<=n;i++)
    {
        dp[i]=(dp[i-1]+dp[i-2])%mod;
    }
    printf("%d",dp[n]);
    return 0;
}

12、树状数组
假设a[n]为原始数组,c[n]为树状数组

核心:low_bit函数

int low_bit(int x)
{
	return x&(-x);
}

树状数组初始化操作:

void init()
{
    for(int i=1;i<=n;i++)
    {
        c[i]+=a[i];
        if(i+low_bit(i)<=n)
        {
            c[i+low_bit(i)]+=c[i];
        }
    }
}

求某个点的前缀和:

int sum(int x)
{
    int r=0;
    while(x)
    {
        r+=c[x];
        x-=low_bit(x);
    }
    return r;
}

修改某个点的值:

void add(int x,int v)
{
    while(x<=n)
    {
        c[x]+=v;
        x+=low_bit(x);
    }
}

emmmmm最后只希望能水个省三…

  • 12
    点赞
  • 81
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值