蓝桥杯 2022 省赛 C++ B组

填空问题

九进制转十进制

九进制正整数 (2022)9 转换成十进制等于多少?
import java.util.Scanner;
// 1:无需package
// 2: 类名必须Main, 不可修改

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        String st = "2022";
        System.out.println(Integer.parseInt(st,9));
        scan.close();
    }
}

顺子日期

public class 顺子日期 {
    //顺子数组
    static String[] str={"012","123","234","345","456","567","678","789"};

    public static void main(String[] args) {
        int n=20220101;
        int cnt=0;
        while (n<20221231){
            if(judge(n)){
                if(isShun(n+"")) cnt++;
            }
            n++;
        }
        System.out.println(cnt);
    }

    //判断是否是合理日期
    static boolean judge(int x){
        int[] w={0,31,28,31,30,31,30,31,31,30,31,30,31}; //2022年每个月的天数
        int m = x%10000/100;//月份
        int d = x%100;//天数
        if(d<=w[m]&&d>0) return true; //天数控制在合理范围内
        return false;
    }

    //判断是否有顺子日期
    static boolean isShun(String s){
        for(int i=0; i<str.length; i++){
            if(s.contains(str[i])) return true;
        }
        return false;
    }
}

程序设计

刷题统计

小明决定从下周一开始努力刷题准备蓝桥杯竞赛。他计划周一至周五每天 做 a 道题目, 周六和周日每天做 b 道题目。请你帮小明计算, 按照计划他将在 第几天实现做题数大于等于 n 题?

思路

从数据范围上面看,我们不能模拟,我们可以计算出一周小明的刷题数 w=5a+2b。那么可以在O(1)时间内计算出需要完整的刷多少周的题目,剩下的刷不到一周的题目再单独模拟。

时间复杂度:O(1)

import java.util.Scanner;

public class Main {
   public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        //三数
        Long a = sc.nextLong();
        Long b = sc.nextLong();
        Long n = sc.nextLong();
        //计算一周的刷题量
        Long week_sum = a * 5 + b * 2;
        if ( n % week_sum == 0 ){
            System.out.println( ( n / week_sum ) * 7 ); //如果能被整除,直接输出
        }else {
            //过了 i个整周
            Long i = n / week_sum;
            //剩余num题没刷,这些题不必花一整周的时间
            Long num = n - i * week_sum;
            //从周一开始刷题
            int day = 0;
            int j = 0;          //还需刷j天的题
            long sum = 0L;    //这些天的刷题数
            while (sum < num){
                if (day < 5) sum+=a;
                else sum+=b;
                j++; day++;
                day = day%7;
            }
            System.out.println( i * 7 + j ) ;
        }
    }
}

修剪灌木

思路

模拟一下,就可以发现其中的规律。爱丽丝只有向左走或者向右走,之后再回到原点,要寻找每颗灌木最高长到多少,只用看向左走再向右走长得高,还是向右走在向左走长得高。

import java.util.Scanner;

public class 修剪灌木 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n=sc.nextInt();
        for(int i=1; i<=n; i++){
            int max=Math.max(i-1,n-i)*2;
            System.out.println(max);
        }
    }
}

X 进制减法

思路

首先要明白这个x进制是如何计算的。

先看例子,321,最低位为二进制,所以还是1,第二位为十进制,所以需要往前进位,2转为十进制就为4,第三位数,也需要向第二位进位,所以321->65: 3*(10*2)+2*2+1=65。我们要让A,B两个的差值较大,就要让进制尽可能小,每个位置的进制等于A,B两个数的较大值+1.

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
// 1:无需package
// 2: 类名必须Main, 不可修改


public class Main {
   static int N=1000010;
    static int[]A=new int[N];//存储每一位
    static int[]B=new int[N];//存储每一位
    static int[]x;//进制表
    static int max;//最大进制

    static final BufferedReader cin = new BufferedReader(new InputStreamReader(System.in));
    public static void main(String[]args) throws IOException {
        max=Integer.parseInt(cin.readLine());
        //A数组
        int ma=Integer.parseInt(cin.readLine());
        String[]as=cin.readLine().split(" ");
        for (int i =ma-1,j=0; i>=0; i--)A[i]=Integer.parseInt(as[j++]);;

        //B数组
        int mb=Integer.parseInt(cin.readLine());
        String[]bs=cin.readLine().split(" ");
        for (int i =mb-1,j=0; i>=0; i--)B[i]=Integer.parseInt(bs[j++]);

        //初始化进制表
        x=new int[Math.max(ma,mb)+1];

        //录入进制表
        int m = Math.max(ma, mb);
        //计算各个位置进制为,取2、a[i]+1、b[i]+1的最大值
        for (int i = 0; i < m; i ++) {
            x[i] = Math.max(Math.max(A[i] + 1, B[i] + 1), 2);
        }
        //计算A B
        f(A,B,x,ma,mb);

    }
    static int mod=1000000007;

    static void f(int[]A,int[]B,int[]x,int lenA,int lenB){
        long res=0;
        long a=0,b=0;
        for (int i =lenA-1; i>=0;i--)a=(a*x[i]+A[i])%mod;
        for (int i =lenB-1; i>=0;i--)b=(b*x[i]+B[i])%mod;
        System.out.println((a-b+mod)%mod);
    }
}

统计子矩阵

思路

首先,我们有一个查询子矩阵和的需求,肯定需要使用预处理二维前缀和来优化查询。

确立一个子矩阵,需要一个左上角(x1,y1)和一个右下角(x2,y2),如果进行暴力枚举的话复杂度将会是O(n^4),会超时不可取。那么如何进行优化,对于x1,x2我们任然进行暴力枚举,然后我们用L表示y1,R表示y2。由于数组内不存在负数,所以随着指针右移,L也一定单调向右移动。这就转化成了一维数组内求和不大于K的数目。枚举R为右边界,此时子矩阵左上角坐标为(x1,L),右下角坐标为(x2,R),如求得总和大于K,L向右移动,统计当前R作为右边界的答案数量为R-L+1,这样双指针做大的复杂度为O(n)。

这样使用双指针整体的复杂度为O(n^3)。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class Main {
    static int N=510;
    static int[][] a = new int[N][N];
    static int[][] s = new int[N][N];
    static int n,m,k;
    static BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

    public static void main(String[] args) throws IOException {
        String[] ts = br.readLine().split(" ");
        n = Integer.parseInt(ts[0]);
        m = Integer.parseInt(ts[1]);
        k = Integer.parseInt(ts[2]);
        for(int i = 1; i <= n; i++){
            ts = br.readLine().split(" ");
            for(int j = 1; j <=m; j++){
                a[i][j] = Integer.parseInt(ts[j-1]);
                s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i][j];
            }
        }
        long res = 0;
        //枚举上下的边
        for(int x1=1; x1 <= n; x1++){
            for(int x2 = x1; x2<=n; x2++){
                //枚举l和r
                int l=1;
                int r=1;
                while(r<=m){
                    while(l<=r && !check(x1,l,x2,r)) l++;
                    res += (long)r-l+1;
                    r++;
                }
            }
        }
        System.out.println(res);
    }
    static boolean check(int x1, int y1, int x2, int y2){
        return s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1] <= k;
    }
}

积木画

思路

状态压缩dp模型,虽然N很大,但总共只有两行,只需要考虑结尾的插入情况即可。定义f[i][j]为已经排好了前i-1列,且第i列的状态为j的方案数。当j为0时表示第i列上面下面都没有摆放积木。为1时表示上面未摆放,下面拜访了积木。为2时上面摆了,下面没摆。为3时上面下面都摆放了积木。

由定义可知最终答案为f[0][3]

如何进行初始化?当n为0时,可以视为完全摆好的情况,则f[0][3]=1,当n为1时,只有一种摆法f[1][3]=1.

接下来考虑如何进行状态转移?首先考虑f[i][0],因为0表示上下都未摆放积木。而状态定义前i-1列已经摆好了,则转移方程:f[i][0]=f[i-1][3]

然后考虑f[i-1][1]如何转移:

1表示我们第 i列是下面摆放了上面未摆放,也就是下面突出了一格,我们考虑如何才会产生这样的效果。转移时不应该从 i − 1 列转移,而应该是从f [ i − 2 ] [ 3 ] 转移。

综上我们有两种情况可以得到f [ i ] [ 1 ] f[i][1]f[i][1],转移方程为:f[i][1] = (f[i−1][0]+f[i−1][2])

分析 f[i][2]]其实和 f[i][1]同理,反过来就行,方程:f[i][1] = (f[i−1][0]+f[i−1][1])

最后f[i][3],

可以从f [ i − 1 ] [ 1 ] 和f [ i − 1 ] [ 2 ]转移,综上:dp[i-1][0]+dp[i-1][1]+dp[i-1][2]+dp[i-1][3]

import java.util.Scanner;

public class Main {
    static int N=10000010;
    static int mod=1000000007;
    static int[][] dp=new int[N][4];

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n=sc.nextInt();
        int[][] dp=new int[n+1][4];
        dp[1][0]=1;
        dp[1][3]=1; //初始化
        for(int i=2;i<=n;i++){
            dp[i][0]=dp[i-1][3]; //情况为0
            dp[i][1] = (dp[i-1][0]+dp[i-1][2])%mod; //情况为1
            dp[i][2] = (dp[i-1][0]+dp[i-1][1])%mod; //情况为2
            dp[i][3]=(((dp[i-1][0]+dp[i-1][1])%mod+dp[i-1][2])%mod+dp[i-1][3])%mod; //情况为3
        }
        System.out.println(dp[n][3]);
        sc.close();
    }
}

上面的有一些方法不能通过,贴一个直接找规律的吧。

import java.util.Scanner;

public class Main {
     public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        int N=10000010;
        int mod=1000000007;
        long[] dp = new long[N];
        dp[1]=dp[0]=1;
        dp[2]=2;
        for (int i = 3; i <=n ; i++) {
            dp[i]=(2*dp[i-1]+dp[i-3])%mod;
        }
        System.out.println(dp[n]);
        scan.close();
    }
}

扫雷

李白打酒加强版

砍竹子

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王水最甜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值