2018蓝桥杯初赛


并不包含全部


第几个幸运数

题意:

只含有质因子3,5,7的是幸运数字,前几个幸运数字是3 5 7 9 15 21 25 27 35 45,
现在问你59084709587505是第几个幸运数字。

思路:

用优先队列存储,每次取出最小值。然后把他乘上3,5,7的结果入队。
记录出队的次数,当出队的是题目要求的数字则计数完成。
注意去重,因为3乘上5可以得到15,5乘上3也可以得到15,不去重答案会错而且爆内存。

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main(){
    int n=59084709587505LL;
    priority_queue<int,vector<int>,greater<int> >q;
    map<int,int>mark;
    q.push(1);
    int a[3]={3,5,7};
    int ans=0;
    while(1){
        int x=q.top();
        q.pop();
        if(x==n)break;
        for(int i=0;i<3;i++){
            if(!mark[x*a[i]]){
                mark[x*a[i]]=1;
                q.push(x*a[i]);
            }
        }
        ans++;
    }
    cout<<ans<<endl;
    return 0;
}

明码

题意:

在这里插入图片描述

思路:

题目说16x16的像素,每个字节存8位,那么一行就是两个字节,
转成二进制输出即可,程序输入如下:
在这里插入图片描述

完整文字为:“九的九次方等于多少?”
输出387420489即可

code(转换):

#include<bits/stdc++.h>
using namespace std;
const int maxm=50;
int a[maxm][maxm];
signed main(){
    int T=10;
    while(T--){
        for(int i=0;i<16;i++){
            int x,y;
            cin>>x>>y;
            int now=0;
            for(int j=7;j>=0;j--){
                a[i][now++]=(x>>j&1);
            }
            for(int j=7;j>=0;j--){
                a[i][now++]=(y>>j&1);
            }
        }
        for(int i=0;i<16;i++){
            for(int j=0;j<16;j++){
                if(a[i][j])cout<<a[i][j];
                else cout<<' ';
            }
            cout<<endl;
        }
    }
    return 0;
}

测试次数

题意:

在这里插入图片描述

思路:

d[i][j]表示i层j个球最坏运气下需要的最少次数

枚举丢的层数k:
如果k层碎了,则d[i][j]=d[k-1][j-1]
如果k层没碎,那么继续测试k+1到i层,等价于测试1到i-(k+1)+1,一共i-k层,那么d[i][j]=d[i-k][j]

dp一下就行了

code:

#include<bits/stdc++.h>
using namespace std;
const int n=1000,m=3;
int d[n+5][m+5];
signed main(){
    for(int i=1;i<=n;i++){
        for(int j=0;j<=m;j++){
            d[i][j]=1e9;
        }
    }
    for(int i=1;i<=n;i++){//枚举层数
        for(int j=1;j<=m;j++){//枚举数量
            for(int k=1;k<=i;k++){//枚举丢的层数
                int t=max(d[k-1][j-1],d[i-k][j]);//碎和不碎
                d[i][j]=min(d[i][j],t+1);
            }
        }
    }
    cout<<d[n][m]<<endl;
    return 0;
}

耐摔指数(((

题意:

题目意思和上面的测试次数一样,
但是这题是多组输入,每次输入一个n,表示有n层,3个手机最坏需要测试多少次
数据范围:n<=1e4

解法:

这题的数据范围比上一题大,像上一题一样dp的话会超时。

令F(x)为扔x次在最坏情况下所能测出的最大层数

当只有一个手机的时候:
只能从第一层开始逐层测试,因此F1(x)=x

当有两个手机的时候:
第一个手机随便从那一层开始扔
如果碎了,则第二个手机只能从1开始扔,接下来变为F1(x-1)
如果没碎,则还是两个手机,变成原问题的子问题,接下来所需次数为F2(x-1)
加上刚刚扔的这一层,得到F2(n)=F1(n-1)+1+F2(n-1)+1

三个手机的时候同理:F3(n)=F2(n-1)+1+F3(n-1)+1

递推一下就行了

code:

#include<bits/stdc++.h>
using namespace std;
const int maxm=1e4+5;
int f[maxm];
int ff[maxm];
int fff[maxm];
signed main(){
    for(int i=1;;i++){
        f[i]=i;
        ff[i]=f[i-1]+ff[i-1]+1;
        fff[i]=ff[i-1]+fff[i-1]+1;
        if(fff[i]>maxm)break;
    }
    int n;
    while(cin>>n){
        int ans=0;
        while(fff[ans]<n)ans++;
        cout<<ans<<endl;
    }
    return 0;
}

螺旋折线

题意:

在这里插入图片描述

思路:

找一下坐标轴上的点的距离规律,然后硬算


方格计数

题意:

在这里插入图片描述

思路:

看到数据范围50000,以为N^2暴力不行了,可能有什么数学方法。
过了一会想起来本地跑就行了,虽然很慢但是跑出来还是没问题的。

枚举第一象限内的50000*50000的所有点,判断是否在圆内,统计数量之后乘上4就是答案

答案为7853781044。

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main(){
//    cout<<"7853781044"<<endl;
//    return 0;
    int ans=0;
    for(int i=1;i<=50000;i++){
        for(int j=1;j<=50000;j++){
            if(i*i+j*j>50000LL*50000)break;
            ans++;
        }
    }
    cout<<ans*4<<endl;
    return 0;
}

复数幂

题意:

在这里插入图片描述

思路:

直接上java大数,自己写一个复数类就行了。

提交的时候要删掉package!!!

code:

import java.util.*;
import java.math.*;
class Complex{//复数类
	BigInteger a,b;//实部和虚部
	Complex(BigInteger a,BigInteger b){
		this.a=a;
		this.b=b;
	}
	void mul(Complex t) {//this和复数t相乘
		BigInteger x=(this.a.multiply(t.a)).subtract(this.b.multiply(t.b));
		BigInteger y=(this.a.multiply(t.b)).add(this.b.multiply(t.a));
		this.a=x;
		this.b=y;
	}
}
public class Main{
	public static void main(String[] xx) {
		Scanner cin=new Scanner(System.in);
		int n=123456;
		Complex a=new Complex(BigInteger.valueOf(2),BigInteger.valueOf(3));
		Complex ans=new Complex(BigInteger.ONE,BigInteger.ZERO);
		while(n>0) {
			if(n%2==1) {
				ans.mul(a);
			}
			a.mul(a);
			n>>=1;
		}
		//输出实部
		System.out.print(ans.a);
		//如果虚部是正数则需要输出一个加号
		if(ans.b.compareTo(BigInteger.ZERO)>=0)System.out.print("+");
		//输出虚部
		System.out.print(ans.b);
		//记得输出一个i
		System.out.println("i");
	}
}

字母阵列

题意:

在这里插入图片描述

思路:

显然是dfs
要注意的是这题dfs的方向是不能改变的,因为序列的方向固定,
意思是如果一开始是向右走的,那么之后也都要向右走,dfs的过程记录一下方向就行了。
枚举每个点作为起点dfs即可。

答案为41

code:

#include<bits/stdc++.h>
using namespace std;
const int maxm=105;
const int n=100;
const string t="LANQIAO";
char s[maxm][maxm];
int a[8]={-1,1,0,0,-1,-1,1,1};
int b[8]={0,0,-1,1,-1,1,1,-1};
int ans;
void dfs(int x,int y,int cur,int dir){
    if(s[x][y]!=t[cur])return ;
    if(cur==t.size()-1){
        ans++;
        return ;
    }
    int xx=x+a[dir];
    int yy=y+b[dir];
    dfs(xx,yy,cur+1,dir);
}
signed main(){
//    cout<<"41"<<endl;
//    return 0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            cin>>s[i][j];
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            for(int k=0;k<8;k++){
                dfs(i,j,0,k);
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}

倍数问题

题意:

在这里插入图片描述

思路:

n很大,O(n3)枚举或者O(n2)的枚举都不行,观察到k只有1e3,显然是按模k余数分类然后处理。
因为只需要取三个数,模k的同余数超过3个的话,把多余的去掉即可(其实也只有模k等于0的数能取到3个,其余最多两个)。
然后遍历0到k-1,枚举当前同余数选择几个,然后去找其他数补全即可。

我的代码最大复杂度为O(3*k2)

code:

#include<bits/stdc++.h>
using namespace std;
const int maxm=1e3+5;
vector<int>g[maxm];
signed main(){
    int n,k;cin>>n>>k;
    for(int i=1;i<=n;i++){
        int x;cin>>x;
        g[x%k].push_back(x);//按模k同余分类
    }
    for(int i=0;i<k;i++){
        sort(g[i].begin(),g[i].end(),[](int a,int b){return a>b;});//从大到小排序
        if(g[i].size()>=3)g[i].resize(3);//超过3个就把多余的去掉
    }
    int ans=0;
    for(int i=0;i<k;i++){//O(k)
        int sum=0;
        int cnt=0;//选择的数量
        for(int j=0;j<(int)g[i].size();j++){//枚举g[i]选择个数,max=3
            sum+=g[i][j];
            cnt++;
            if(cnt==1){//还需要两个
                for(int t=0;t<k;t++){//O(k)
                    if(t==i)continue;//跳过i自身,因为i选多个的情况上面会循环到
                    if(g[t].empty())continue;
                    int temp=sum+g[t][0];
                    int need=(k-temp%k)%k;
                    if(need==i)continue;//跳过i自身,因为i选多个的情况上面会循环到
                    if(need==t)continue;//跳过t自身,因为t选多个的情况也会循环到
                    for(int v:g[need]){//O(1)
                        ans=max(ans,temp+v);
                        break;
                    }
                }
            }else if(cnt==2){//O(1)
                if((k-sum%k)%k==i)continue;
                for(int v:g[(k-sum%k)%k]){
                    ans=max(ans,sum+v);
                    break;
                }
            }else if(cnt==3){//O(1)
                if(sum%k==0)ans=max(ans,sum);
            }
        }
    }
    cout<<ans<<endl;
    return 0;
}

堆的计数

题意:

在这里插入图片描述
n<=1e5

思路:

根节点显然已经确定,只能填1
设d[i]为根位置为i的子树的完全二叉堆方案数(这个i不是数字i,而是二叉树的节点编号)
设左子树大小为Lsize,从n-1个点中挑选Lsize个点的方案数为C(n-1,Lsize)
那么d[i]=C(n-1,Lsize)*d[i*2]*d[i*2+1]
dp一下就行了

code:

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=1e5+5;
const int mod=1e9+9;
int fac[maxm];
int sz[maxm];
int d[maxm];
int n;
int ppow(int a,int b,int mod){
    int ans=1%mod;a%=mod;
    while(b){
        if(b&1)ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}
int C(int n,int m){
    if(m>n||m<0)return 0;
    int up=fac[n];
    int down=fac[m]*fac[n-m]%mod;
    return up*ppow(down,mod-2,mod)%mod;
}
int cal(int x){//计算每个位置子树大小
    if(x>n)return 0;
    return sz[x]=cal(x*2)+cal(x*2+1)+1;
}
int dp(int x){
    if(sz[x]<=1)return 1;
    if(d[x])return d[x];
    return d[x]=C(sz[x]-1,sz[x*2])*dp(x*2)%mod*dp(x*2+1)%mod;
}
signed main(){
    cin>>n;
    fac[0]=1;
    for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%mod;
    sz[1]=n;
    cal(1);
    cout<<dp(1)<<endl;
    return 0;
}

发布了457 篇原创文章 · 获赞 37 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_44178736/article/details/104997849
今日推荐