Java && C++ 实现第十二届蓝桥杯 C++ B组 省赛真题(希望能和各位佬能一起讨论算法题:讨论群:99979568)

山水有相逢 来日皆可期
告辞

虽然是C++组的,但是和Java组的差不了两道题,大家都可以看一看
如有错误,还请佬 评论或私信指出(写的稍些急)
等后面在补充补充解题思路,在补一套C++实现(已完成,简单题未补C++版本代码)
再也不肝博客了
/(ㄒoㄒ)/~~

(由于上传图片有限制,如果描述不清楚点击手写题解)
手写题解字丑,但是小编感觉会比文字看的直观一些(其实是不会用数学公式)

编程题测试数据

手写题解(如果访问出错,请刷新访问的页面即可(Nebo的问题))

A:空间
B:卡片
C:直线
D:货物摆放
E:路径
F:时间显示
G:砝码称重
H:杨辉三角形
I:双向排序
J:括号序列

当前页面的编程题均在C语言网成功运行
C语言网有各届蓝桥杯的题库
第十二届蓝桥杯编程题测试
刷题集

在这里插入图片描述

A:空间

在这里插入图片描述

package LanqiaobeiExam._12CB;

/**
 * ClassName: A空间
 * Package: LanqiaobeiExam._12CB
 *
 * @DATE: 2022/3/21 15:01
 * Author: asleep
 */
public class A空间 {  //1B = 8bit(位)    32位 = 4B  1MB = 1024KB = 1024 * 1024B
    public static void main(String[] args) {    //67108864
        System.out.println(256 * 1024 * 1024 / 4);
    }
}

B:卡片

在这里插入图片描述

package LanqiaobeiExam._12CB;

/**
 * ClassName: B卡片
 * Package: LanqiaobeiExam._12CB
 *
 * @DATE: 2022/3/21 15:19
 * Author: asleep
 */
public class B卡片 {  //3181      从1开始循环数字,每个数字循环每一位,对应数字卡片 -1,减到 0 表示当前数字不能拼成
    public static void main(String[] args) {
        int[] count = new int[10];
        for (int i = 0; i < 10; i++) {
            count[i] = 2021;
        }
        int index = 0;
        A:
        while (true) {
            int temp = ++index;
            while (temp != 0) {
                if (count[temp % 10] > 0) {
                    count[temp % 10]--;
                } else {
                    System.out.println(index - 1);
                    break A;
                }
                temp /= 10;
            }
        }

    }
}

C:直线

在这里插入图片描述
请添加图片描述

package LanqiaobeiExam._12CB;

import java.util.*;

/**
 * ClassName: C直线
 * Package: LanqiaobeiExam._12CB
 *
 * @DATE: 2022/3/21 15:29
 * Author: asleep
 */
public class C直线 {	//40257
    public static class Node {
        int x, y;

        public Node(int x, int y) {
            this.x = x;
            this.y = y;
        }

        double x1, y1;

        public Node(double x1, double y1) {
            this.x1 = x1;
            this.y1 = y1;
        }


    }

    public static class Line {
        int a, b, c;

        public Line(int a, int b, int c) {
            this.a = a;
            this.b = b;
            this.c = c;
        }

        @Override
        public boolean equals(Object o) {
            Line line = (Line) o;
            return a == line.a && b == line.b && c == line.c;
        }

        @Override
        public int hashCode() {
            return Objects.hash(a, b, c);
        }
    }

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

    public static int gcd(int a, int b, int c) {
        return gcd(gcd(a, b), gcd(b, c));
    }

    public static void main(String[] args) {
        //方法一   两点式
        {

            int n = 20, m = 21;
            Set<Line> set = new HashSet<>();
            Node[] node = new Node[1000];
            int index = 0;
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < m; j++) {
                    node[index++] = new Node(i, j);
                }
            }
            for (int i = 0; i < index; i++) {
                for (int j = i + 1; j < index; j++) {
                    int a = node[i].y - node[j].y;//系数
                    int b = node[j].x - node[i].x;
                    int c = node[i].y * (node[i].x - node[j].x) - node[i].x * (node[i].y - node[j].y);
                    int g = gcd(Math.abs(a), Math.abs(b), Math.abs(c));
                    set.add(new Line(a / g, b / g, c / g));
                }
            }
            System.out.println(set.size());

        }

        //方法二:  y = kx+b 确定直线
        {

            List<Integer> points = new ArrayList<>();
            Set<String> ans = new HashSet<>();
            for (int i = 0; i < 20; i++) {
                for (int j = 0; j < 21; j++) {
                    points.add(i * 100 + j); //存xy
                }
            }
            int size = points.size();
            for (int i = 0; i < size; i++) {
                int node1 = points.get(i);
                int x1 = node1 / 100, y1 = node1 % 100;
                for (int j = i + 1; j < size; j++) {
                    int node2 = points.get(j);
                    int x2 = node2 / 100, y2 = node2 % 100;
                    int up = y1 - y2, down = x1 - x2;
                    if (down == 0) {
                        ans.add("x = " + x1);
                        continue;
                    }
                    int c1 = gcd(up, down);
                    String K = up / c1 + " " + down / c1;
                    int Up = y1 * down - x1 * up;
                    int c2 = gcd(Up, down);
                    String B = Up / c2 + " " + down / c2;
                    ans.add(K + " " + B);
                }
            }
            System.out.println(ans.size());

        }

        //方法三:
        {
            //两点式两个点交换后为一条新的直线,需要除 2
            Set<Line> set = new HashSet<>();
            int n = 20, m = 21;
            for (int x1 = 0; x1 < n; x1++) {
                for (int y1 = 0; y1 < m; y1++) {
                    for (int x2 = 0; x2 < n; x2++) {
                        for (int y2 = 0; y2 < m; y2++) {
                            if (x1 == x2 && y1 == y2) { //两个相同的点不能确定直线
                                continue;
                            }
                            int a =y1 - y2, b = x2 - x1;
                            int c = x1 * (y2 - y1) - y1 * (x2 - x1);
                            int g = gcd(Math.abs(a), Math.abs(b), Math.abs(c));
                            set.add(new Line(a / g, b / g, c / g));
                        }
                    }
                }
            }
            System.out.println(set.size() / 2);

        }

    }
}

#include "iostream"
#include "algorithm"
#include "cmath"

using namespace std;

struct Node {   //40257
    double k, b;
    bool operator< (const Node& node) const {  //自定义排序方法,先按照k的大小排序,k相等时按照b的大小排序
        if (k != node.k) {
            return k < node.k;
        }
        return b < node.b;
    }
}nodes[200000];;

int main() {
    int index= 0;   //遍历两个xy
    for (int x1 = 0; x1 < 20; x1++) {
        for (int y1 = 0; y1 < 21; y1++) {
            for (int x2 = 0; x2 < 20; x2++) {
                for (int y2 = 0; y2 < 21; y2++) {
                    if (x1 == x2) { //斜率是0,一共有二十条,后面直接加
                        continue;
                    }
                    double k = (double)(y2 - y1) / (x2 - x1);
                    double b = y1 - k * x1;
                    nodes[index++] = {k, b};
                }
            }
        }
    }
    sort(nodes, nodes + index);
    int res = 1;    //默认第一个就存在了
    for (int i = 1; i < index; i++) {   //小数是有误差的,当误差比10的-8次方小,我们就认为是一条直线
        if (fabs(nodes[i].k - nodes[i - 1].k) > 1e-8 || fabs(nodes[i].b - nodes[i - 1].b) > 1e-8) {
            res++;
        }
    }
    cout << res + 20;

    return 0;
}

D:货物摆放

在这里插入图片描述

package LanqiaobeiExam._12CB;

/**
 * ClassName: D货物摆放
 * Package: LanqiaobeiExam._12CB
 *
 * @DATE: 2022/3/21 17:00
 * Author: asleep
 */
public class D货物摆放 {    //2430
    public static void main(String[] args) {    //直接遍历 n 会炸,我们这里遍历 n 的约数,相对复杂度会低很多
        long n = 2021041820210418L;
        long[] res = new long[10000];
        int index = 0;
        for (long i = 1L; i <= n / i; i++) {
            if (n % i == 0) {
                res[index++] = i;
                if (i * i != n) {
                    res[index++] = n / i;
                }
            }
        }
        int count = 0;
        for (int i = 0; i < index; i++) {
            for (int j = 0; j < index; j++) {
                if (n % (res[i] * res[j]) == 0) {
                    count++;
                }
            }
        }
        System.out.println(count);
    }
}

E:路径

在这里插入图片描述

Dijkstra最短路径

package LanqiaobeiExam._12CB;

import java.util.Arrays;

/**
 * ClassName: E路径
 * Package: LanqiaobeiExam._12CB
 *
 * @DATE: 2022/3/21 17:08
 * Author: asleep
 */
public class E路径 {  //10266837
    static int[][] map = new int[2022][2022];
    public static int gcd(int a, int b) {
        return b == 0 ? a : gcd(b, a % b);
    }
    public static int dijkstra() {
        int[] distance = new int[2022];
        Arrays.fill(distance, 0xffffff);
        boolean[] visited = new boolean[2022];
        distance[1] = 0;
        for (int i = 1; i < 2022; i++) {
            int temp = -1;
            for (int j = 1; j < 2022; j++) {
                if (!visited[j] && (temp == -1 || distance[j] < distance[temp])) {
                    temp = j;
                }
            }
            visited[temp] = true;
            for (int j = 1; j < 2022; j++) {
                distance[j] = Math.min(distance[j], distance[temp] + map[temp][j]);
            }
        }
        return distance[2021];
    }


    public static void main(String[] args) {
        for (int i = 1; i < 2022; i++) {
           for (int j = 1; j < 2022; j++) {
               if (i != j) {
                   if (Math.abs(i - j) <= 21) {
                       map[i][j] = i * j / gcd(i, j);
                       map[j][i] = map[i][j];
                   } else {
                       map[i][j] = 0xffffff;
                       map[j][i] = map[i][j];
                   }
               }
           }
        }
        System.out.println(dijkstra());



    }
}

#include <cstring>
#include "cmath"
#include "iostream"

using namespace std;

const int N = 2030;
int n = 2021;
int dis[N], map[N][N], visited[N];


int Dijkstra() {    //10266837
    memset(dis, 0x3f3f3f3f, sizeof(dis));
    dis[1] = 0;
    for (int i = 1; i <= n; i++) {
        int temp = -1;
        for (int j = 1; j <= n; j++) {
            if (!visited[j] && (temp == -1 || dis[j] < dis[temp])) {
                temp = j;
            }
        }
        visited[temp] = 1;
        for (int j = 1; j <= n; j++) {
            dis[j] = min(dis[j], dis[temp] + map[temp][j]);
        }
    }
    return dis[n];

}

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

int main() {
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            if (fabs(i - j) <= 21) {
                map[i][j] = i * j / gcd(i, j);
            } else {
                map[i][j] = 0xffffff;
            }
        }
    }
    cout << Dijkstra();
    return 0;
}

F:时间显示

在这里插入图片描述

package LanqiaobeiExam._12CB;

import java.util.Scanner;

/**
 * ClassName: F时间显示
 * Package: LanqiaobeiExam._12CB
 *
 * @DATE: 2022/3/21 20:18
 * Author: asleep
 */
public class F时间显示 {    //无需考虑天数,只要一天内的时间,直接取余算 时分秒 即可
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        long n = sc.nextLong();
        n /= 1000;
        long second = n % 60;
        n /= 60;
        long minute = n % 60;
        n /= 60;
        long hour = n % 24;
        n /= 24;
        if (hour < 10) {
            System.out.print("0" + hour + ":");
        } else {
            System.out.print(hour + ":");
        }
        if(minute < 10) {
            System.out.print("0" + minute + ":");
        } else {
            System.out.print(minute + ":");
        }
        if (second < 10) {
            System.out.print("0" + second);
        } else {
            System.out.print(second);
        }
    }
}

G:砝码称重

在这里插入图片描述

请添加图片描述

package LanqiaobeiExam._12CB;

import java.util.Scanner;

/**
 * ClassName: G砝码称重
 * Package: LanqiaobeiExam._12CB
 *
 * @DATE: 2022/3/21 20:33
 * Author: asleep
 */
public class G砝码称重 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[] num = new int[n + 1];
        int sum = 0;
        for (int i = 1; i < n + 1; i++) {
            num[i] = sc.nextInt();
            sum += num[i];
        }
        boolean[][] dp = new boolean[101][100001];
        dp[0][0] = true;
        for (int i = 1; i < n + 1; i++) {
            for (int j = 0; j < sum + 1; j++) {
                dp[i][j] = dp[i - 1][j] || dp[i - 1][Math.abs(j - num[i])] || dp[i - 1][j + num[i]];
            }
        }
        int res = 0;
        for (int i = 1; i < sum + 1; i++) {
            if (dp[n][i]) {
                res++;
            }
        }
        System.out.println(res);


    }
}

#include "iostream"
#include "cmath"

using namespace std;

int num[101];
bool dp[101][100001];   //这里防止 j+num[i] 超过范围
int main() {
    int n;
    int sum = 0;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> num[i];
        sum += num[i];
    }
    dp[0][0] = true;
    for (int i = 1; i <= n; i++) {
        for (int j = 0; j <= sum; j++) {
            dp[i][j] = dp[i - 1][j] || dp[i - 1][(int)fabs(j - num[i])] || dp[i - 1][j + num[i]];
        }
    }
    int res = 0;
    for (int i = 1; i<= sum; i++) {
        if (dp[n][i]) {
            res++;
        }
    }
    cout    << res;
    return 0;
}

H:杨辉三角形

在这里插入图片描述

请添加图片描述

package LanqiaobeiExam._12CB;

import java.util.Scanner;

/**
 * ClassName: H杨辉三角形
 * Package: LanqiaobeiExam._12CB
 *
 * @DATE: 2022/3/21 21:42
 * Author: asleep
 */
public class H杨辉三角形 {
    static int N;
    public static long C(long n, long m) {
        long res = 1L;
        for (long i = n, j = 1; j <= m; i--, j++) {
            res = res * i / j;
            if (res > N) {
                return res;
            }
        }
        return res;
    }
    public static boolean dichotomy(int k) {
        long left = k * 2, right = N;
        if (left > right) { // 已经没有可能了,杨辉三角右半边的数字都比左半边的数字对称
            return false;
        }
        while (left < right) {
            long mid = (right - left) / 2 + left;
            if (C(mid, k) >= N) {
                right = mid;
            } else {
                left = mid + 1;
            }
        }
        if (C(right, k) == N) {
//            System.out.println(right + " " + k);
            System.out.println(right * (right + 1) / 2 + k + 1);
            return true;
        }
        return false;
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        N = sc.nextInt();
//        System.out.println(C(116, 8));
        for (int i = 16; i >= 0; i--) {
            if (dichotomy(i)) {
                break;
            }
        }

    }
}

#include "iostream"

using namespace std;

int N;

long C(long a, long b) {
    long res = 1;
    for (long i = a, j = 1; j <= b; i--, j++) {
        res = res * i / j;
        if (res > N) {
            break;
        }
    }
    return res;
}

bool dichotomy(int k) {
    long left = 2 * k, right = N;
    if (left > right) {
        return false;
    }
    while (left < right) {
        long mid = (left + right) / 2;
        if (C(mid, k) >= N) {
            right = mid;
        } else {
            left = mid + 1;
        }
    }
    if (C(right, k) == N) {
        cout << right * (right + 1) / 2 + k + 1;
        return true;
    }
    return false;
}



int main() {
    cin >> N;
    for (int i = 16; i >= 0; i--) {
        if (dichotomy(i)) {
            break;
        }
    }
    return 0;
}

I:双向排序

在这里插入图片描述

请添加图片描述

package LanqiaobeiExam._12CB;

import org.w3c.dom.Node;

import java.util.ArrayList;
import java.util.Scanner;

/**
 * ClassName: I双向排序
 * Package: LanqiaobeiExam._12CB
 *
 * @DATE: 2022/3/22 19:04
 * Author: asleep
 */
public class I双向排序 {
    public static class Node {
        int p;
        int q;

        public Node(int p, int q) {
            this.p = p;
            this.q = q;
        }
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int m = sc.nextInt();
        ArrayList<Node> list = new ArrayList<>();
        for (int i = 0; i < m; i++) {
            int p = sc.nextInt();
            int q = sc.nextInt();
            if (p == 0) {
                while (list.size() > 0 && list.get(list.size() - 1).p == 0) {   //相同前半段操作,选下标最靠右的
                    q = Math.max(q, list.get(list.size() - 1).q);
                    list.remove(list.size() - 1);
                }
                while (list.size() >= 2 && list.get(list.size() - 2).q <= q) {  //如果当前操作的下标比上次前半段操作下标靠右,则前两次操作无效
                    list.remove(list.size() - 1);
                    list.remove(list.size() - 1);
                }
                list.add(new Node(0, q));
            } else if (list.size() > 0) {
                while (list.size() > 0 && list.get(list.size() - 1).p == 1) {   //相同后半段操作,选下标最靠左的
                    q = Math.min(q, list.get(list.size() - 1).q);
                    list.remove(list.size() - 1);
                }
                while (list.size() >= 2 && list.get(list.size() - 2).q >= q) {  //当前操作下标比上次后半段操作靠左,则前两次操作无效
                    list.remove(list.size() - 1);
                    list.remove(list.size() - 1);
                }
                list.add(new Node(1, q));
            }
        }
//        for (Node node : list) {
//            System.out.print(node.p + " " + node.q + "    ");
//        }

        int left = 1, right = n, num = n;   //确定一下插入值,和未插入范围的左右边界
        int[] res = new int [n + 1];
        for (int i = 0; i < list.size(); i++) { //开始插入值,每次插入值都 -1
            if (list.get(i).p == 0) {
                while (right > list.get(i).q && left <= right) {    //前半段操作,从后向前插入,一定要保证插入范围在下标右面
                    res[right--] = num--;
                }
            } else {
                while (left < list.get(i).q && left <= right) { //后半段,从前向后插入,一定要保证插入范围在下标左面
                    res[left++] = num--;
                }
            }
            if (left > right) { //如果未插入范围已经没有了,则插入完成
                break;
            }
        }                           //第0次是前半段操作,第1次是后半段操作,……以此类推
        if (list.size() % 2 == 0) { //如果上面操作完还存在未插入的范围,则按照规律插入
            while (left <= right) {
                res[right--] = num--;
            }
        } else {
            while (left <= right) {
                res[left++] = num--;
            }
        }
//        System.out.println();
        for (int i = 1; i <= n; i++) {
            System.out.print(res[i] + " ");
        }
    }
}

#include "iostream"

using namespace std;

const int N = 100010;
int n, m;
pair<int, int> nums[N];
int res[N];

int main() {
    cin >> n >> m;
    int top = 0;
    while (m--) {   //筛选操作
        int p, q;
        cin >> p >> q;
        if (p == 0) {
            while (top && nums[top].first == 0) {
                q = max(q, nums[top--].second);
            }
            while (top >= 2 && nums[top - 1].second <= q) {
                top -= 2;
            }
            nums[++top] = {p, q};
        } else if (top) {
            while (top && nums[top].first == 1) {
                q = min(q, nums[top--].second);
            }
            while (top >= 2 && nums[top - 1].second >= q) {
                top -= 2;
            }
            nums[++top] = {p, q};
        }
    }
    int num = n, left = 1, right = n;   //数字放入数组
    for (int i = 1; i<= top; i++) {
        if (nums[i].first == 0) {
            while (right > nums[i].second && left <= right) {
                res[right--] = num--;
            }
        } else {
            while (left < nums[i].second && left <= right) {
                res[left++] = num--;
            }
        }
        if (left > right) {
            break;
        }
    }
            //这里是当上面还存在未操作的范围,接下面默认填充上
    if (top % 2 == 1) { //前半段降序操作   (第一步降序,第二步升序,降序,升序……   如果最后一步是奇数,就是降序操作)
        while (left <= right) {
            res[left++] = num--;
        }
    } else {
        while (left <= right) {
            res[right--] = num--;
        }
    }
    for (int i = 1; i <= n; i++) {
        cout << res[i] << " ";
    }
    return 0;
}

J:括号序列

在这里插入图片描述

请添加图片描述

package LanqiaobeiExam._12CB;

import java.util.Scanner;

/**
 * ClassName: J括号序列
 * Package: LanqiaobeiExam._12CB
 *
 * @DATE: 2022/3/22 21:36
 * Author: asleep
 */
public class J括号序列 {
    static int MOD = 1000000007;
    private static long calc(String s) {
        int n = s.length();
        long[][] dp = new long[n + 2][n + 2];   //+2  是因为dp状态转移方程需要  n+1的数(其实是懒得特判)
        char[] str = s.toCharArray();
        dp[0][0] = 1;
        for (int i = 1; i <= n; i++) {
            if (str[i - 1] == '(') {
                for (int j = 1; j <= n; j++) {
                    dp[i][j] = dp[i - 1][j - 1];
                }
            } else {
                dp[i][0] = (dp[i - 1][0] + dp[i - 1][1]) % MOD; //这一步用原状态转移式,新状态转移式子会越界
                for (int j = 1; j <= n; j++) {
                    dp[i][j] = (dp[i - 1][j + 1] + dp[i][j - 1]) % MOD;
                }
            }
        }
        for (int i = 0; i <= n; i++) {  //一定存在解,最差dp[n][n]也是解
            if (dp[n][i] > 0) { //  其实有一种可能是dp本来有值,但是 %MOD 后为0,显然测试用例没有这种情况(如果有这种情况加个boolean数组记录一下即可)
                return dp[n][i];
            }
        }
        return -1;
    }


    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String s = sc.next();
        int n = s.length();
        long left = calc(s);
        char[] str = new StringBuilder(s).reverse().toString().toCharArray();
        for (int i = 0; i < n; i++) {
            if (str[i] == '(') {
                str[i] = ')';
            } else {
                str[i] = '(';
            }
        }
        String ss = new String(str);

        long right = calc(ss);
        System.out.println(left * right % MOD);
    }


}

#include <algorithm>
#include "iostream"
#include "cstring"

using namespace std;

const int N = 5010, MOD = 1e9 + 7;

int n;
char str[N];
int dp[N][N];

long calc() {
    memset(dp, 0, sizeof dp);
    dp[0][0] = 1;
    for (int i = 1; i <= n; i++) {
        if (str[i - 1] == '(') {
            for (int j = 1; j <= n; j++) {
                dp[i][j] = dp[i - 1][j - 1];
            }
        } else {
            dp[i][0] = (dp[i - 1][0] + dp[i - 1][1]) % MOD;
            for (int j = 1; j <= n; j++) {
                dp[i][j] = (dp[i - 1][j + 1] + dp[i][j - 1]) % MOD;
            }
        }
    }
    for (int i = 0; i <= n; i++) {
        if (dp[n][i]) {
            return dp[n][i];
        }
    }
    return -1;
}

int main() {
    cin >> str;
    n = strlen(str);
    long left = calc();
    int a = 10;
    reverse(str, str + n);
    for (int i = 0; i < n; i++) {
        if (str[i] == '(') {
            str[i] = ')';
        } else {
            str[i] = '(';
        }
    }
    long right = calc();
    cout << left * right % MOD << endl;
    return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值