蓝桥杯试题

目录

测试次数

快速排序

递增三元组

 螺旋折线

日志统计

全球变暖

明码

乘积尾零

砝码称重

杨辉三角

路径

时间显示

直线

货物摆放

空间

卡片

回文日期

子串分值和

七段码 

成绩统计

蛇型数组

跑步锻炼

门牌制作

既约分数

最大公共子串

方格分割

承压计算

后缀表达式

包子凑数

日期问题

等差数列

完全二叉树的权值

分巧克力

等差素数列

特别数的和

迷宫

数列求值

数的分解

七夕礼物

组队

年号字串

  •  测试次数

题目描述

x星球的居民脾气不太好,但好在他们生气的时候唯一的异常举动是:摔手机。

各大厂商也就纷纷推出各种耐摔型手机。x星球的质监局规定了手机必须经过耐摔测试,并且评定出一个耐摔指数来,之后才允许上市流通。x星球有很多高耸入云的高塔,刚好可以用来做耐摔测试。塔的每一层高度都是一样的,与地球上稍有不同的是,他们的第一层不是地面,而是相当于我们的2楼。如果手机从第7层扔下去没摔坏,但第8层摔坏了,则手机耐摔指数=7。特别地,如果手机从第1层扔下去就坏了,则耐摔指数=0。如果到了塔的最高层第n层扔没摔坏,则耐摔指数=n,为了减少测试次数,从每个厂家抽样3部手机参加测试。

某次测试的塔高为1000层,如果我们总是采用最佳策略,在最坏的运气下最多需要测试多少次才能确定手机的耐摔指数呢?

分析:

  • 把 M 层楼 / N 个手机的问题转化成一个函数 F(M,N),其中楼层数 M 和手机数 N 是函数的两个参数,而函数的值则是最优解的最大尝试次数
  • 第一个手机扔出的位置在第 X (1≤X≤M) 层,会出现两种情况,第一个手机没碎,那么剩余的 M−X 层楼,剩余 N 个手机,可以转变为函数:F(M−X,N)+1,1≤X≤M,第一个手机碎了,那么只剩下从 1 层到 X−1 层楼需要尝试,剩余的手机数量是 N−1,可以转变为函数:F(X−1,N−1)+1,1≤X≤M

F(M,N)=Min(Max(F(M−X,N)+1,F(X−1,N−1)+1)),1≤X≤M

public class Main {

    static int getMinSteps(int num, int floorNum) {
        if (num < 1 || floorNum < 1)
            return 0;

        // 存储num个手机,floorNum层楼条件下的最优化尝试次数
        int[][] cache = new int[num + 1][floorNum + 1];
        // 每个元素初始化成最大的尝试次数,即只有一个手机
        for (int i = 1; i <= num; i++)
            for (int j = 1; j <= floorNum; j++)
                cache[i][j] = j;

        for (int n = 2; n <= num; n++)
            for (int m = 1; m <= floorNum; m++)
                for (int k = 1; k < m; k++)
                    // 扔手机的楼层从1到m枚举一遍,如果当前算出的尝试次数小于上一次算出的尝试次数,则取代上一次的尝试次数。
                    cache[n][m] = Math.min(cache[n][m], 1 + Math.max(cache[n - 1][k - 1], cache[n][m - k]));

        return cache[num][floorNum];
    }

    public static void main(String[] args) {
        System.out.println(getMinSteps(3, 1000));
    }
}
  • 快速排序

问题描述
  用递归来实现快速排序(quick sort)算法。快速排序算法的基本思路是:假设要对一个数组a进行排序,且a[0] = x。首先对数组中的元素进行调整,使x放在正确的位置上。同时,所有比x小的数都位于它的左边,所有比x大的数都位于它的右边。然后对于左、右两段区域,递归地调用快速排序算法来进行排序。
  输入格式:输入只有一行,包括若干个整数(不超过10个),以0结尾。
  输出格式:输出只有一行,即排序以后的结果(不包括末尾的0)。
输入输出样例
样例输入

5 2 6 1 7 3 4 0
样例输出
1 2 3 4 5 6 7

分析:

1.先从数列中取出一个数作为基准数。

2.分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。

3.再对左右区间重复第二步,直到各区间只有一个数。


import java.util.Scanner;
import java.util.Random;
public class Main{
    public static int quickSelect(int a[], int l, int r, int k) {
        Random rand = new Random();
        int p = rand.nextInt(r - l + 1) + l;
        int x = a[p];
        int tmp = a[p]; a[p] = a[r]; a[r] = tmp;
        int i = l, j = r;
        while(i < j) {
            while(i < j && a[i] < x) i++;//小于基准值,往左放
            if(i < j) {
                a[j] = a[i];
                j--;
            }
            while(i < j && a[j] > x) j--;
            if(i < j) {
                a[i] = a[j];
                i++;
            }
        }
        a[i] = x;
        p = i;
        if(i - l + 1 == k) return a[i];
        if(i - l + 1 < k) return quickSelect(a,i+1,r,k-i+l-1); //将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
        else return quickSelect(a, l, i - 1, k);
    }
    public static void main(String args[]) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        int a[]=new int[110];
        for(int i=0;i<n;i++)
        {
            a[i]=scan.nextInt();
        }
        System.out.println(quickSelect(a, 0, n-1, 5));
    }
}
  • 递增三元组

问题描述

给定三个整数数组
A = [A1, A2, ... AN],
B = [B1, B2, ... BN],
C = [C1, C2, ... CN],
请你统计有多少个三元组(i, j, k) 满足:

  1. 1 <= i, j, k <= N
  2. Ai < Bj < Ck

【输入格式】
第一行包含一个整数N。
第二行包含N个整数A1, A2, ... AN。
第三行包含N个整数B1, B2, ... BN。
第四行包含N个整数C1, C2, ... CN。

对于30%的数据,1 <= N <= 100
对于60%的数据,1 <= N <= 1000
对于100%的数据,1 <= N <= 100000 0 <= Ai, Bi, Ci <= 100000

【输出格式】
一个整数表示答案

【输入样例】
3
1 1 1
2 2 2
3 3 3

【输出样例】
27

分析:暴力破解即可

import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        int count = 0;
        Scanner sc = new Scanner(System.in);
        int N = sc.nextInt();
        int arr1[] = new int[N];
        int arr2[] = new int[N];
        int arr3[] = new int[N];
        for (int i = 0; i < N; i++) {
            arr1[i]= sc.nextInt();
        }
        for (int i = 0; i < N; i++) {
            arr2[i]= sc.nextInt();
        }
        for (int i = 0; i < N; i++) {
            arr3[i]= sc.nextInt();
        }
        for (int i = 0; i < arr1.length; i++) {
            for (int j = 0; j < arr2.length; j++) {
                for (int k = 0; k < arr3.length; k++) {
                    if (arr1[i] < arr2[j] && arr2[j] < arr3[k]) {
                        count++;
                    }
                }
            }
        }
        sc.close();
        System.out.println(count);
    }
}
  •  螺旋折线

如图p1.pgn所示的螺旋折线经过平面上所有整点恰好一次。
对于整点(X, Y),我们定义它到原点的距离dis(X, Y)是从原点到(X, Y)的螺旋折线段的长度。

例如dis(0, 1)=3, dis(-2, -1)=9

给出整点坐标(X, Y),你能计算出dis(X, Y)吗?

【输入格式】
X和Y

对于40%的数据,-1000 <= X, Y <= 1000
对于70%的数据,-100000 <= X, Y <= 100000
对于100%的数据, -1000000000 <= X, Y <= 1000000000

【输出格式】
输出dis(X, Y)

【输入样例】
0 1

【输出样例】
3

分析:

  • 由图可得,主要分析各个象限的拐点即可

第一象限的拐点坐标——dx = max(abs(x), abs(y)),dy = dx
第二象限的拐点坐标——dx = -max(abs(x), abs(y)),dy = -dx
第三象限的拐点坐标——dx = -max(abs(x), abs(y)),dy = dx - 1
第四象限的拐点坐标——dx = max(abs(x), abs(y)),dy = -dx

  • 由图可得,距离与拐点的关系

第一象限(abs(dx)+abs(dy))^2
第二象限(abs(dx)+abs(dy)) * (abs(dx)+abs(dy) - 1)
第三象限(abs(dx)+abs(dy))^2
第四象限(abs(dx)+abs(dy)) * (abs(dx)+abs(dy) + 1)


import java.util.Scanner;

public class Main {
    static int x, y;
    static int df = 0;
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        x = in.nextInt();
        y = in.nextInt();

        if (x > 0 && y >= 0) {//第一象限
            int dx = Math.max(x, y);
            int dy = dx;//拐点坐标
            df = (dx + dy) * (dx + dy);//拐点
            if (x < dx) {
                df -= (dx - x);
            }
            if (y < dy) {
                df += (dy - y);
            }
        } else if (x >= 0 && y < 0) {
            int dx = Math.max(Math.abs(x), Math.abs(y));
            int dy = -dx;
            df = (dx + Math.abs(dy)) * (dx + Math.abs(dy) + 1);
            if (x < dx) {
                df += (dx - x);
            }
            if (y > dy) {
                df -= (y - dy);
            }
        } else if (x < 0 && y <= 0) {
            int dx = -Math.max(Math.abs(x), Math.abs(y));
            int dy = dx + 1;
            df = (Math.abs(dx) + Math.abs(dy)) * (Math.abs(dx) + Math.abs(dy));
            if (x > dx) {
                df -= (x - dx);
            }
            if (y > dy) {
                df += (y - dy);
            }
        } else  if (x <= 0 && y > 0) {
            int dx = -Math.max(Math.abs(x), Math.abs(y));
            int dy = -dx;
            df = (Math.abs(dx) + Math.abs(dy)) * (Math.abs(dx) + Math.abs(dy) - 1);
            if (x > dx) {
                df += (x - dx);
            }
            if (y < dy) {
                df -= (y - dy);
            }
        }
        System.out.println(df);
    }
}
  • 日志统计

【题目描述】
小明维护着一个程序员论坛。现在他收集了一份"点赞"日志,日志共有N行。其中每一行的格式是:

ts id

表示在ts时刻编号id的帖子收到一个"赞"。

现在小明想统计有哪些帖子曾经是"热帖"。如果一个帖子曾在任意一个长度为D的时间段内收到不少于K个赞,小明就认为这个帖子曾是"热帖"。

具体来说,如果存在某个时刻T满足该帖在[T, T+D)这段时间内(注意是左闭右开区间)收到不少于K个赞,该帖就曾是"热帖"。

给定日志,请你帮助小明统计出所有曾是"热帖"的帖子编号。

【输入格式】
第一行包含三个整数N、D和K。
以下N行每行一条日志,包含两个整数ts和id。

对于50%的数据,1 <= K <= N <= 1000
对于100%的数据,1 <= K <= N <= 100000 0 <= ts <= 100000 0 <= id <= 100000

【输出格式】
按从小到大的顺序输出热帖id。每个id一行。

【输入样例】
7 10 2
0 1
0 10
10 10
10 1
9 1
100 3
100 3

【输出样例】
1

3

 解析:主要采用尺取法

1.[ T ,T+D )是一个“窗口”,随着时间T的增长,窗口不断往后滑动,符合尺取法的通向扫描

2.尺取法的思路:按时间从小到大处理每一个帖子,当处理到T时刻的帖子时,窗口[T-D,T)之前的帖子已经失效了,对当前窗口的统计没有效果

import java.util.Arrays;
import java.util.Scanner;
 
public class Main {
    public static void main(String args[]) {
        int n, d, k;
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        d = sc.nextInt();
        k = sc.nextInt();
        resou arr[] = new resou[n];
        for (int i = 0; i < n; i++) {
            int time = sc.nextInt();
            int id = sc.nextInt();
            arr[i] = new resou(time, id);
        }
        Arrays.sort(arr); // 对其进行排序
        int ID = arr[0].id;
        boolean flag =false;
        for (int i = 0; i < n; i++) {
            if (i + k - 1 < n && arr[i + k - 1].id == ID && arr[i + k - 1].ts - arr[i].ts < d && !flag  )
            {
                System.out.println(ID);
                flag = true;
            } else if (arr[i].id != ID)
            {
                ID = arr[i].id;
                flag = false;
                i = i - 1;
            }
        }
    }
}
 
class resou implements Comparable<resou> //创建resou类,继承Comparable排序类
{
    int ts, id;
    resou(int ts, int id)
    {
        this.ts = ts;
        this.id = id;
    }
 
    @Override
    public int compareTo(resou com) {//自定义对象同样需要继承Comparable接口,并重写compareTo方法
        if (id == com.id) // 先对id做比较其次id相同对ts做比较
            return ts - com.ts;
        else
            return id - com.id;
    }
}


  • 全球变暖

【题目描述】

你有一张某海域NxN像素的照片,".“表示海洋、”#"表示陆地,如下所示:

其中"上下左右"四个方向上连在一起的一片陆地组成一座岛屿。例如上图就有2座岛屿。
由于全球变暖导致了海面上升,科学家预测未来几十年,岛屿边缘一个像素的范围会被海水淹没。具体来说如果一块陆地像素与海洋相邻(上下左右四个相邻像素中有海洋),它就会被淹没。

例如上图中的海域未来会变成如下样子:

请你计算:依照科学家的预测,照片中有多少岛屿会被完全淹没。

【输入格式】
第一行包含一个整数N。 (1 <= N <= 1000)
以下N行N列代表一张海域照片。

照片保证第1行、第1列、第N行、第N列的像素都是海洋。

【输出格式】
一个整数表示答案。

【输入样例】

【输出样例】
1

分析:

  • 用dfs找出总共的岛屿数目,对于dfs方法中,首先需判断i,j是否超出矩阵边界,避免下标越界情况出现。之后将该字符处的访问标志为置1表示已访问过,然后依次判断该字符上右下左四个方向是否存在陆地且未曾访问过,如果存在则把找到的符合条件的字符作为当前字符,继续深搜,这样属于同一个岛屿的陆地访问标志都变成了1,且岛屿数只加过1次。
  • 用两个for循环淹没岛屿(四面一面邻海就要被淹没) ,利用一个数组来表示淹没还是陆地
  • 再用dfs找出淹没后还有多少个岛屿,前后相减就是被淹没的岛屿数
import java.util.Scanner;

public class Main {
    static int n;
    static int[][] dfs_vis;
    static int dir[][] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        sc.nextLine();
        dfs_vis = new int[n][n];
        char[][] map = new char[n][n];
        for (int i = 0; i < n; i++) {
            String s = sc.nextLine();
            map[i] = s.toCharArray();
        }
        //找岛屿
        int start = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (map[i][j] == '#' && dfs_vis[i][j] == 0) {
                    dfs_vis[i][j] = 1;
                    dfs(i, j, map);
                    start++;
                }
            }
        }

        //淹没
        int vis[][] = new int[n][n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (map[i][j] == '#') {
                    vis[i][j] = 1;
                    for (int k = 0; k < 4; k++) {
                        int nx = i + dir[k][0];
                        int ny = j + dir[k][1];
                        if (check(nx, ny, n) && map[nx][ny] == '.' && vis[nx][ny] == 0) {
                            //vis[nx][ny] = 1;
                            map[i][j] = '.';
                            break;
                        }
                    }
                }
            }
        }

        //找淹没后剩余的岛屿
        dfs_vis = new int[n][n];
        int end = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (map[i][j] == '#' && dfs_vis[i][j] == 0) {
                    dfs_vis[i][j] = 1;
                    dfs(i, j, map);
                    end++;
                }
            }
        }

        System.out.println(start);
        System.out.println(end);
        System.out.println(start - end);
    }

    private static void dfs(int i, int j, char[][] map) {
        for (int k = 0; k < 4; k++) {
            int nx = i + dir[k][0];
            int ny = j + dir[k][1];
            if (check(nx, ny, map.length) && map[nx][ny] == '#' && dfs_vis[nx][ny] == 0) {
                dfs_vis[nx][ny] = 1;
                dfs(nx, ny, map);
            }
        }
    }

    private static boolean check(int nx, int ny, int n) {

        return nx >= 0 && nx < n && ny >= 0 && ny < n;
    }
}
  • 明码

题目描述

汉字的字形存在于字库中,即便在今天,16点阵的字库也仍然使用广泛。16点阵的字库把每个汉字看成是16x16个像素信息。并把这些信息记录在字节中。一个字节可以存储8位信息,用32个字节就可以存一个汉字的字形了。把每个字节转为2进制表示,1表示墨迹,0表示底色。每行2个字节,一共16行,布局是:
    第1字节,第2字节
    第3字节,第4字节
    ....
    第31字节, 第32字节

这道题目是给你一段多个汉字组成的信息,每个汉字用32个字节表示,这里给出了字节作为有符号整数的值。题目的要求隐藏在这些信息中。你的任务是复原这些汉字的字形,从中看出题目的要求,并根据要求填写答案。
这段信息是(一共10个汉字):

4 0 4 0 4 0 4 32 -1 -16 4 32 4 32 4 32 4 32 4 32 8 32 8 32 16 34 16 34 32 30 -64 0 
16 64 16 64 34 68 127 126 66 -124 67 4 66 4 66 -124 126 100 66 36 66 4 66 4 66 4 126 4 66 40 0 16 
4 0 4 0 4 0 4 32 -1 -16 4 32 4 32 4 32 4 32 4 32 8 32 8 32 16 34 16 34 32 30 -64 0 
0 -128 64 -128 48 -128 17 8 1 -4 2 8 8 80 16 64 32 64 -32 64 32 -96 32 -96 33 16 34 8 36 14 40 4 
4 0 3 0 1 0 0 4 -1 -2 4 0 4 16 7 -8 4 16 4 16 4 16 8 16 8 16 16 16 32 -96 64 64 
16 64 20 72 62 -4 73 32 5 16 1 0 63 -8 1 0 -1 -2 0 64 0 80 63 -8 8 64 4 64 1 64 0 -128 
0 16 63 -8 1 0 1 0 1 0 1 4 -1 -2 1 0 1 0 1 0 1 0 1 0 1 0 1 0 5 0 2 0 
2 0 2 0 7 -16 8 32 24 64 37 -128 2 -128 12 -128 113 -4 2 8 12 16 18 32 33 -64 1 0 14 0 112 0 
1 0 1 0 1 0 9 32 9 16 17 12 17 4 33 16 65 16 1 32 1 64 0 -128 1 0 2 0 12 0 112 0 
0 0 0 0 7 -16 24 24 48 12 56 12 0 56 0 -32 0 -64 0 -128 0 0 0 0 1 -128 3 -64 1 -128 0 0 

思路:

  • 每两个为一组,十进制转化为2进制
toBinaryString(int i)
  • 负数用补码表示
  • str=str.substring(int beginIndex);截取掉str从首字母起长度为beginIndex的字符串,将剩余字符串赋值给str;
public class Main {

    public static void main(String[] args) {
        int array[] = new int[] {4, 0, 4, 0 ,4 ,0 ,4, 32, -1, -16, 4, 32, 4, 32, 4,
                32, 4, 32, 4, 32, 8, 32, 8, 32, 16, 34, 16, 34, 32, 30, -64, 0,
                16, 64, 16, 64, 34 ,68 ,127, 126 ,66 ,-124, 67, 4, 66, 4 ,66 ,-124,
                126 ,100 ,66 ,36, 66, 4, 66, 4, 66, 4, 126, 4, 66, 40, 0 ,16,4,0,4,
                0,4,0,4,32,-1,-16,4,32,4,32,4,32,4,32,4,32,8,32,8,32,16,34,16,34,32,
                30,-64,0,0,-128,64,-128,48,-128,17,8,1,-4,2,8,8,80,16,64,32,64,-32,
                64,32,-96,32,-96,33,16,34,8,36,14,40,4,4,0,3,0,1,0,0,4,-1,-2,4,0,4,
                16,7,-8,4,16,4,16,4,16,8,16,8,16,16,16,32,-96,64,64,16,64,20,72,62,
                -4,73,32,5,16,1,0,63,-8,1,0,-1,-2,0,64,0,80,63,-8,8,64,4,64,1,64,0,
                -128,0,16,63,-8,1,0,1,0,1,0,1,4,-1,-2,1,0,1,0,1,0,1,0,1,0,1,0,1,0,5,
                0,2,0,2,0,2,0,7,-16,8,32,24,64,37,-128,2,-128,12,-128,113,-4,2,8,12,
                16,18,32,33,-64,1,0,14,0,112,0,1,0,1,0,1,0,9,32,9,16,17,12,17,4,33,
                16,65,16,1,32,1,64,0,-128,1,0,2,0,12,0,112,0,0,0,0,0,7,-16,24,24,48,
                12,56,12,0,56,0,-32,0,-64,0,-128,0,0,0,0,1,-128,3,-64,1,-128,0,0 };
        for(int i=0; i < array.length; i++) {
            if(array[i] < 0) {
                String bin1 = Integer.toBinaryString(array[i]).replace("0", " ");
                String bin2 = bin1.substring(24).replace("0", " ");
                if(i % 2 != 0) {//两个字节为一组
                    System.out.println(bin2);
                }else {
                    System.out.print(bin2);
                }
            }else {
                String bin3 = Integer.toBinaryString(array[i]).replace("0", " ");
                String ary[] = new String[] {"       ","      ","     ","    ","   ","  "," ",""};
                if(i % 2 != 0) {
                    System.out.println(ary[bin3.length()-1]+bin3);
                }else {
                    System.out.print(ary[bin3.length()-1]+bin3);
                }
            }
        }
    }

}
  • 乘积尾零

题目描述:
如下的10行数据,每行有10个整数,请你求出它们的乘积的末尾有多少个零?
5650 4542 3554 473 946 4114 3871 9073 90 4329
2758 7949 6113 5659 5245 7432 3051 4434 6704 3594
9937 1173 6866 3397 4759 7557 3070 2287 1453 9899
1486 5722 3135 1170 4014 5510 5120 729 2880 9019
2049 698 4582 4346 4427 646 9742 7340 1230 7683
5693 7015 6887 7381 4172 4341 2909 2027 7355 5649
6701 6645 1671 5978 2704 9926 295 3125 3878 6785
2066 4247 4800 1578 6652 4616 1113 6205 3264 2915
3966 5291 2904 1285 2193 1428 2265 8730 9436 7074
689 5510 8243 6114 337 4096 8199 7313 3685 211
注意:需要提交的是一个整数,表示末尾零的个数。不要填写任何多余内容。

分析:

暴力破解,特别注意为防止越界,为防止越界,每次保留后三位,只有为0时,才会出现

import java.util.Scanner;

public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int num [] = new int [100];
        int cnt = 0;
        for (int i = 0; i < 100; i++) {
            num[i] = sc.nextInt();
        }
        for (int i = 1; i < num.length; i++) {
            num[i] *= num[i-1];
            while(num[i] % 10 == 0)
            {
                cnt++;
                num[i] /= 10;
            }
            num[i] %= 1000;//为防止越界,每次保留后三位,只有为0时,才会出现
        }
        System.out.println(cnt);
    }

}
  • 砝码称重

 分析:

例如:我们输入3个砝码,分别是1,4,6

当输入第一个砝码(1)时,可以称出重量为:1

再输入第二个砝码(4)时,可以称出的重量为:1,(4 - 1),(4 + 1),4

当前可以称出的重量有:1,3,4,5

再输入第三个砝码(6)时,可以称出的重量为:1,3,4,5,(6 - 1),(6 + 1),(6 - 3),(6 + 3),(6 - 4),(6 + 4),(6 - 5),(6 + 5),6

当前可以称出的重量有:1,2,3,4,5,6,7,9,10,11

故每次加入一个砝码,这时只需要将上一个状态的每个可以被称出的重量与新的砝码进行组合,即得出当前砝码数量可以被称出的所有重量一直到最后一个砝码加入,最后一个状态就得出了所有可以被称出的重量

  • 杨辉三角

每个数字都是上面两个数字的和

方法一:

import java.util.Scanner;

public class Main1{
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[][] nums = new int[n][n];
        for(int i=0;i<n;i++){//每一行第一个均为1
            nums[i][0] = 1;
        }
        nums[1][1] = 1;//第二行也是一
        if(n>2){
            for(int i=2;i<n;i++){
                for(int j=1;j<=i;j++){
                    nums[i][j] = nums[i-1][j]+nums[i-1][j-1];//每个数字都是上面两个数字的和
                }
            }
        }
        for(int i=0;i<n;i++){
            for(int j=0;j<=i;j++){
                System.out.print(nums[i][j]+" ");
            }
            System.out.println();
        }
    }
}

方法二:时间复杂度低

number = number * (i-j) / (j+1)

import java.util.Scanner;

public class Main2 {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int[][] nums = new int[n][n];
        for(int i=0;i<n;i++){
            Long number = (long) 1;
            for(int j=0;j<=i;j++){
                System.out.print(number+" ");
                // number = number*(i-j)/(j+1)是重点
                //数据过大可能溢出,用Long型
                number = (Long)number * (i-j)/(j+1);
            }
            System.out.println();
        }
    }

}

  • 路径

【问题描述】
   小蓝学习了最短路径之后特别高兴,他定义了一个特别的图,希望找到图中的最短路径。
小蓝的图由 2021 个结点组成,依次编号 1 至 2021。对于两个不同的结点 a, b,如果 a 和 b 的差的绝对值大于 21,则两个结点之间没有边相连;如果 a 和 b 的差的绝对值小于等于 21,则两个点之间有一条长度为 a 和 b 的最小公倍数的无向边相连。

例如:结点 1 和结点 23 之间没有边相连;结点 3 和结点 24 之间有一条无向边,长度为 24;结点 15 和结点 25 之间有一条无向边,长度为 75。请计算,结点 1 和结点 2021 之间的最短路径长度是多少。

思路:

  • 使用邻接矩阵将图存储
  • 最小公倍数—>最大公约数—>辗转相除法
  • 弗洛伊德算法

public class Main {
    public static void main(String[] args) {
        int len = 2021;
        int maxValue=1000000000;
        int[][] matrix = new int[len][len];
        for (int i = 0; i < len; i++) {//构造邻接矩阵
            for (int j = i + 1; j < len; j++) {
                if (j <= 21 + i) {
                    matrix[i][j] = lcm(j + 1, i + 1);
                    matrix[j][i]=lcm(j + 1, i + 1);
                }else{
                    matrix[i][j]=maxValue;
                    matrix[j][i]=maxValue;
                }
            }
        }
        floyd(matrix);
        System.out.println(matrix[1-1][len-1]);

    }
    public static void floyd(int[][] dis){
        int length;
        for (int k = 0; k <dis.length ; k++) {
            for (int i = 0; i < dis.length; i++) {
                for (int j = 0; j < dis.length; j++) {
                    length=dis[i][k]+dis[k][j];
                    if(length < dis[i][j]){
                        dis[i][j]=length;//更新路径
                    }
                }
            }
        }
    }
    //最大公约数
    public static int gcd(int m, int n) {
        return n == 0 ? m : gcd(n, m % n);
    }

    //最小公倍数
    public static int lcm(int m, int n) {
        return m * n / gcd(m, n);
    }
}
  • 时间显示

小蓝要和朋友合作开发一个时间显示的网站。在服务器上,朋友已经获取了当前的时间,用一个整数表示,值为从 1970年 11 月 11 日 00:00:00 到当前时刻经过的毫秒数。现在,小蓝要在客户端显示出这个时间。小蓝不用显示出年月日,只需要显示出时分秒即可,毫秒也不用显示,直接舍去即可。

给定一个用整数表示的时间,请将这个时间对应的时分秒输出。

分析:

  • 时=n/1000/60/60%24

  • 分=n/1000/60%60

  • 秒=n/1000%60


import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        long time = scanner.nextLong();
        //获取时分秒
        long hours = time / 1000 / 60 / 60 % 24;
        long minutes = time / 1000 / 60 % 60;
        long seconds = time / 1000 % 60;
        //按照输出格式完成输出
        if(hours < 10) {
            System.out.print("0" + hours + ":");
        }else {
            System.out.print(hours + ":");
        }
        if(minutes < 10) {
            System.out.print("0" + minutes + ":");
        }else {
            System.out.print(minutes + ":");
        }
        if(seconds < 10) {
            System.out.print("0" + seconds);
        }else {
            System.out.print(seconds);
        }

    }
}
  • 直线

在平面直角坐标系中,两点可以确定一条直线。给定平面上 20 × 21​ 个整点 {(x, y)|0 ≤ x < 20, 0 ≤ y < 21, x ∈ Z, y ∈ Z}​,即横坐标是 0到 19​ (包含 0​ 和 19​) 之间的整数、纵坐标是 0​ 到 20​ (包含 0​ 和 20​​) 之 间的整数的点。
请问这些点一共确定了多少条不同的直线?

解析:

  • 斜率不存在的直线要另外考虑
  • 将直线存入集合中,重复数据只保留一个
  • k和b可能是小数,而小数在计算机中是有精度的,直接存小数,哪怕用double型也可能不准确。所以我们用分数存储k和b,且要用最简分数表示,否则不好比较是否相同
  • 把坐标存入容器ArrayList中,形式是x*100+y(一个int型的数据)这个数对除以100就是横坐标,对100取余就是纵坐标。
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        HashSet<String> kb=new HashSet<String>();//存放截距与斜率
        int x=0;//横坐标
        int y=0;//纵坐标
        //把坐标以x*100+y的形式存入ArrayList
        HashSet<Integer> al=new HashSet<>();
        for(x=0;x<20;x++){
            for(y=0;y<21;y++){
                al.add(x*100+y);
            }
        }
        List<Integer> arr = new ArrayList<>(al);
        //用i遍历形成的每一条直线,利用点斜式唯一标识一条直线
        for(int i=0;i<x*y;i++){
            int x1=arr.get(i)/100;//点一横坐标
            int y1=arr.get(i)%100;//点一纵坐标
            for(int j=i+1;j<x*y;j++){
                int x2=arr.get(j)/100;//点二横坐标
                int y2=arr.get(j)%100;//点二纵坐标
                /*计算斜率(用分数(kup/kdown)表示)*/
                int kup=y1-y2;
                int kdown=x1-x2;
                if(kdown==0){
                    //kdown=0说明斜率不存在
                    String s="y="+x1;
                    kb.add(s);
                }else{
                    int kgcd=gcd(kup,kdown);//分子分母最大公约数
                    kup=kup/kgcd;
                    kdown=kdown/kgcd;//得到最简分数
                    //计算截距(用分数(bup/bdown)表示)
                    int bup=y1*kdown-kup*x1;
                    int bdown=kdown;
                    int bgcd=gcd(bup,bdown);//分子分母最大公约数
                    bup=bup/bgcd;
                    bdown=bdown/bgcd;//得到最简分数
                    //以kup+" "+Kdown+" "+bup+" "+bdown的形式存储一条直线
                    String s=kup+" "+kdown+" "+bup+" "+bdown;
                    kb.add(s);
                }
            }
        }
        //kb中的元素是不重复的,有重复也只会存储其中一个,因此kb的大小就是直线的个数
        System.out.println(kb.size());
    }
    //辗转相除求最大公约数
    static int gcd(int a, int b) {
        if(b==0){
            return a;
        }
        return gcd(b,a%b);
    }

}
  • 货物摆放

题目描述
小蓝有一个超大的仓库,可以摆放很多货物。现在,小蓝有 n箱货物要摆放在仓库,每箱货物都是规则的正方体。小蓝规定了长、宽、高三个互相垂直的方向,每箱货物的边都必须严格平行于长、宽、高。小蓝希望所有的货物最终摆成一个大的长方体。即在长、宽、高的方向上分别堆 L、W、H 的货物,满足 n =L×W×H。给定 n,请问有多少种堆放货物的方案满足要求。

例如,当 n = 4 时,有以下 6 种方案:1×1×4、1×2×2、1×4×1、2×1×2、2×2×1、4×1×1。

请问,当 n=2021041820210418 (注意有 16 位数字)时,总共有多少种方案?

分析:

  • a、b、c均为n的因数,只需找出n的所有因数,进行枚举
  • 特别注意n为长整型
  • 存入集合去重
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

public class Main {
    public static void main(String[] args) {
        long n = 2021041820210418l;
        long t = n;
        Set<Long> set = new HashSet<>();
        for (long i = 1;i <= t;i++){
            if (n%i==0){//表示i是n的因数
                t = n / i;//t*i=n  依据这个每次缩小遍历的上限,减少时间
                set.add(i);
                set.add(t);
            }
        }
        ArrayList<Long> list = new ArrayList<>(set);
        //利用列表枚举集合中的元素
        int cnt = 0;
        for (int i = 0;i<list.size();i++){
            long a = list.get(i);
            for (int j = 0;j< list.size();j++){
                long b = list.get(j);
                if (a*b>n) continue;//若大于,则不需要第三个,此处可以减少第三重的遍历
                for (int k =0;k< list.size();k++){
                    long c = list.get(k);
                    if (a*b*c==n) cnt++;
                }
            }
        }
        System.out.println(cnt);
    }
}
  • 空间

【问题描述】

小蓝准备用 256MB 的内存空间开一个数组,数组的每个元素都是 32 位
二进制整数,如果不考虑程序占用的空间和维护内存需要的辅助空间,请问
256MB 的空间可以存储多少个 32 位二进制整数?

分析:

位——bit 字节——Bytes 字——words

1 B = 8 b 字节与字的关系与机器的位数有关

  • 卡片

【问题描述】
本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。

小蓝有很多数字卡片,每张卡片上都是数字0到9。小蓝准备用这些卡片来拼一些数,他想从1开始拼出正整数,每拼一个,就保存起来,卡片就不能用来拼其它数了。小蓝想知道自己能从1拼到多少。例如,当小蓝有30张卡片,其中0到9各3张,则小蓝可以拼出1到10,但是拼11时卡片1已经只有一张了,不够拼出11。现在小蓝手里有0到9的卡片各2021张,共2021张,请问小蓝可以从1拼到多少?

解析:

  • 用长度为10的数组,来模拟数值为0~9的卡片,每一张都有2021张
  • 暴力遍历
  • 利用取模,取出每一位数字进行判断
public class Main {
    public static void main(String[] args) {
        int []num = new int[10];
        for (int k = 0;k < 10;k++){//表示卡片的数量
            num[k] = 2021;
        }
        int t;
        int m;
        int flag = 0;
        int i;
        for (i = 1;;i++){
            t = i;
            while (t > 0){//取位数
                m = t % 10;
                if (num[m]==0){
                    flag = 1;//卡片数量为0
                    break;
                }
                num[m]--;
                t/=10;
            }
            if (flag == 1) break;
        }
        System.out.println(i-1);
    }
}

  • 回文日期

题目如下:
2020 年春节期间,有一个特殊的日期引起了大家的注意:2020 年 2 月 2 日。因为如果将这个日期按 “yyyymmdd” 的格式写成一个 8 位数是 20200202,恰好是一个回文数。我们称这样的日期是回文日期。有人表示 20200202 是 “千年一遇” 的特殊日子。对此小明很不认同,因为不到 2 年之后就是下一个回文日期:20211202 即 2021 年 12 月 2 日。也有人表示 20200202 并不仅仅是一个回文日期,还是一个 ABABBABA 型的回文日期。对此小明也不认同,因为大约 100 年后就能遇到下一个 ABABBABA 型的回文日期:21211212 即 2121 年 12 月 12 日。算不上 “千年一遇”,顶多算 “千年两遇”。

给定一个 8 位数的日期,请你计算该日期之后下一个回文日期和下一个 ABABBABA 型的回文日期各是哪一天。

输入描述
输入包含一个八位整数 NN,表示日期。

对于所有评测用例,10000101 \leq N \leq 8999123110000101≤N≤89991231,保证 NN 是一个合法日期的 8 位数表示。

输出描述

输出两个日期类型的回文数ABCDDCBA和ABABBABA

分析:主要思路暴力遍历

  • 判断日期的合法性(年、月、日)
  • 将数字变换成字符串进行回文数比较
substring() 方法返回字符串的子字符串。
parseInt()  解析一个字符串并返回一个整数
import java.util.Scanner;

public class Main {
    public static String str="";
    public static boolean isLeap(int year){//判断闰年,用于日期校验
        return (year%4==0&&year%100!=0)||year%400==0;
    }
    public static boolean check(int a,int b,int c){
        //月份效验
        if(b<1||b>12) return false;
        if(c<1||c>31) return false;
        //日效验
        switch (b){//日期校验
            case 2:if(isLeap(a)&&c>29) return false;
                if(!isLeap(a)&&c>28) return false;
                break;
            case 4:
            case 6:
            case 9:
            case 11:
                if(c>30) return false;
                break;
            default:
                break;
        }
        if(str.charAt(0)==str.charAt(2)//将数字化为字符串进行回文数判断
                &&str.charAt(2)==str.charAt(5)
                &&str.charAt(5)==str.charAt(7)&&
                str.charAt(1)==str.charAt(3)
                &&str.charAt(3)==str.charAt(4)&&
                str.charAt(4)==str.charAt(6))
        {
            return true;
        }
        return false;

    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int cur = scanner.nextInt();
        int sum=0;

        for (int i = cur; i <= 89991231; i++) {
            str="";
            str+=i;
            if(check(Integer.parseInt(str.substring(0,4)),//将某个字符串返回的整数传入检查函数进行判断回文数
                    Integer.parseInt(str.substring(4,6)),
                    Integer.parseInt(str.substring(6,8)))){
                sum++;
                System.out.println(i);
                if(sum==2){//找到两个即停止
                    break;
                }
            }
        }
    }

}
  • 子串分值和

【问题描述】
对于一个字符串 S,我们定义 S 的分值 f(S) 为 S 中出现的不同的字符个 数。例如 f(”aba”) = 2,f(”abc”) = 3, f(”aaa”) = 1。 现在给定一个字符串 S[0…n−1](长度为 n),请你计算对于所有 S 的非空 子串 S[i…j](0≤i≤ j < n),f(S[i…j]) 的和是多少。

【输入格式】
输入一行包含一个由小写字母组成的字符串 S。

【输出格式】
输出一个整数表示答案。

【样例输入】
ababc

【样例输出】
28

思路: 穷举法

  • 求出所有非空子串 
  • 对于每一个非空子串,求出不同字符的个数
  • 求和

HashSet 基于 HashMap 来实现的,是一个不允许有重复元素的集合。

HashSet 允许有 null 值。

HashSet 是无序的,即不会记录插入的顺序。

import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String str = sc.nextLine();
        Set<Character> set = new HashSet<>();
        int sum=0;
        for (int i = 0; i < str.length(); i++) {
            for (int j = 0; j < str.length() - i; j++) {//非空子串的最大长度为原子串的长度
                set.clear();
                for (int k = i; k <= i + j; k++) {
                    set.add(str.charAt(k));//依次取字符串,加入到集合中
                }
                sum+=set.size();
            }
        }
        System.out.println(sum);
    }
}
  • 七段码 

上图给出了七段码数码管的一个图示,数码管中一共有 7 段可以发光的二极管,分别标记为 a, b, c, d, e, f, g。

小蓝要选择一部分二极管(至少要有一个)发光来表达字符。在设计字符的表达时,要求所有发光的二极管是连成一片的。

例如:b 发光,其他二极管不发光可以用来表达一种字符。
例如:c 发光,其他二极管不发光可以用来表达一种字符。这种方案与上一行的方案可以用来表示不同的字符,尽管看上去比较相似。
例如:a, b, c, d, e 发光,f, g 不发光可以用来表达一种字符。
例如:b, f 发光,其他二极管不发光则不能用来表达一种字符,因为发光的二极管没有连成一片。

请问,小蓝可以用七段码数码管表达多少种不同的字符?

思路:并查集的应用

把所有相邻并且发光的二极管合并所形成的路径看成一个集合,看最终产生多少个集合,如果只有一个集合,说明所有发光的二极管连成一片,则可行方案+1。

  • 二维数组表示相邻关系
  • 递归求出一个路径(集合)的根
public class Main {
    public static int N=10;
    public static  int e[][]=new int[N][N];//存储二极管相邻的信息
    public static int f[]=new int[N];//并查集
    public static int ans=0;//记录亮的路径数,为1时符合题目要求
    public static boolean st[]=new boolean[N];//存储二极管的发光情况,true表示发光
    public static void creat(){
        e[1][2]=e[1][6]=1;//表示相邻
        e[2][1]=e[2][7]=e[2][3]=1;
        e[3][2]=e[3][7]=e[3][4]=1;
        e[4][3]=e[4][5]=1;
        e[5][7]=e[5][6]=e[5][4]=1;
        e[6][5]=e[6][7]=e[6][1]=1;
        e[7][2]=e[7][3]=e[7][5]=e[7][6]=1;
    }

    public static int find(int u){//找出某一条路径的根二极管
        if(f[u]==u) return u;
        f[u]=find(f[u]);
        return f[u];
    }

    public static void dfs(int d){
        if(d>7){//所有二极管都检查了一遍,终止条件
            for(int i=1;i<=7;i++){//并查集初始化
                f[i]=i;
            }
            for(int i=1;i<=7;i++){
                for(int j=1;j<=7;j++){
                    if(e[i][j]==1&&st[i]==true&&st[j]==true){//把所有发光且相邻的二极管合并
                        int fx=find(i),fy=find(j);//找出二极管所在路径的根二极管
                        if(fx!=fy){
                            f[fx]=fy;//若两个二极管的根二极管不同,则合并形成一个集合
                        }
                    }

                }
            }
            int k=0;//表示产生的集合的个数
            for(int i=1;i<=7;i++){
                if(st[i] &&f[i]==i)k++;//记录成功的路径数,即产生的集合的个数
            }
            if(k==1) ans++;//只产生一个集合,说明所有的发光二极管是连成一片的
            return;
        }

        st[d]=true;
        dfs(d+1);
        st[d]=false;
        dfs(d+1);
    }

    public static void main(String[] args) {
        creat();
        dfs(1);
        System.out.println(ans);
    }
}
  • 成绩统计

题目描述

小蓝给学生们组织了一场考试,卷面总分为 100 分,每个学生的得分都是一个 0 到 100 的整数。如果得分至少是 60 分,则称为及格。如果得分至少为 85 分,则称为优秀。请计算及格率和优秀率,用百分数表示,百分号前的部分四舍五入保留整 数。
输入描述

输入的第一行包含一个整数 n (1≤n≤104)n\ (1 \leq n \leq 10^4)n (1≤n≤104),表示考试人数。

接下来 n行,每行包含一个 0 至 100 的整数,表示一个学生的得分。
输出描述

输出两行,每行一个百分数,分别表示及格率和优秀率。百分号前的部分 四舍五入保留整数。
 

特别注意四舍五入 

        good = (int)((jg / n) * 100 + 0.5);
        nice = (int)((yx / n) * 100 + 0.5);
public class Main {
    public static void main(String[] args) {
        double jg = 0,yx = 0;
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        int []stu = new int[n];
        for (int i = 0;i < n;i++){
            stu[i] = sc.nextInt();
            if (stu[i] >= 60 && stu[i] <= 100){
                jg++;
                if (stu[i] >= 85) yx++;
            }
        }
        int good = 0,nice = 0;
        good = (int)((jg / n) * 100 + 0.5);
        nice = (int)((yx / n) * 100 + 0.5);
        System.out.println(good + "%");
        System.out.println(nice + "%");
    }
}
  • 蛇型数组

分析:注意边界位置的转化即可

public class Main {
    public static void main(String[] args) {
        int MAX = 100;
        int [][] arr = new int[MAX][MAX];
        arr[0][0] = 1;
        arr[0][1] = 2;
        arr[1][0] = 3;
        int i = 1,j = 0;//从第一行第0列开始
        int flag = 0;
        while(i < MAX-1 & j < MAX-1){
            if(i == 0){//到达第一行,下一个数在原数据的右侧
                arr[0][j+1] = arr[0][j] + 1;
                j++;//同时保证数据的列移动
                flag = 1;
            }
            if(j == 0){//到达第一列,下一个数在原数据的下面
                arr[i+1][0] = arr[i][0] + 1;
                i++;同时保证数据的行移动
                flag = 0;
            }
            if(flag == 0){//表示到达最左边,下一个数向右上方移动
                arr[i-1][j+1] = arr[i][j] + 1;
                i = i - 1;
                j = j + 1;
            }
            if(flag == 1){//表示到达最上面,下一个数向左下方移动
                arr[i+1][j-1] = arr[i][j] + 1;
                i = i + 1;
                j = j - 1;
            }

        }
        for (i = 0;i < MAX;i++){
            for(j = 0;j < MAX;j++){
                System.out.print(arr[i][j] + "\t");
            }
            System.out.println();
        }
    }
}
  • 跑步锻炼

题目描述
小蓝每天都锻炼身体。正常情况下,小蓝每天跑1千米。如果某天是周一或者月初(1日),为了激励自己,小蓝要跑2千米。如果同时是周一或月初,小蓝也是跑2千米。小蓝跑步已经坚持了很长时间,从2000年1月1日周六(含)到2020年10月1日周四(含)。请问这段时间小蓝总共跑步多少千米?

分析:

  • 闰年的判断
  • 星期的运用(% 7) 
public class Main {
    public static void main(String[] args) {
        int day = 0;
        int sum = 0;
        for(int year = 2000;year < 2020;year++){
            for(int month = 1;month <= 12;month++){
                for(int k = 1; k <= day(year,month); k++){
                    int weekday = (day + 6) % 7;
                    if( k == 1 || weekday == 1) sum += 2;
                    else sum++;
                    day++;
                }
            }
        }
        for(int month = 1;month <10;month++){
            for(int k = 1; k <= day(2020,month); k++){
                int weekday = (day + 6) % 7;
                if( k == 1 || weekday == 1) sum += 2;
                else sum++;
                day++;
            }
        }
        System.out.println(sum+2);

    }
    public static boolean year(int year){
        if(((year % 4 == 0)&&(year % 100 != 0))||(year % 400 ==0)) return true;
        return false;
    }
    public static int day(int year,int month){
        if(month == 1 || month == 3 ||month == 5 ||month == 7 ||month == 8 ||month == 10 ||month == 12)return 31;
        if(month == 4 || month == 6 ||month == 9 ||month == 11)return 30;
        if(year(year)) return 29;
        if (!year(year)) return 28;
        return 0;
    }
    public static boolean chu1(int day,int temp){
        if((day - temp == 31) ||(day - temp == 30)||(day - temp == 28)||(day - temp == 29)) return true;
        return false;
    }
}
  • 门牌制作

【问题描述】
小蓝要为一条街的住户制作门牌号。
这条街一共有 2020 位住户,门牌号从 1 到 2020 编号。
小蓝制作门牌的方法是先制作 0 到 9 这几个数字字符,最后根据需要将字
符粘贴到门牌上,例如门牌 1017 需要依次粘贴字符 1、0、1、7,即需要 1 个
字符 0,2 个字符 1,1 个字符 7。
请问要制作所有的 1 到 2020 号门牌,总共需要多少个字符 2?

分析:遍历、取模、计数即可

public class Main {
    public static void main(String[] args) {
        int cnt = 0;
        for(int i = 1;i <= 2020;i++){//遍历
            int m = i;
            while (m > 0){
                if(m % 10 == 2){//取模
                    cnt++;
                }
                m/=10;
            }
        }
        System.out.println(cnt);
    }
}
  • 既约分数

【问题描述】

如果一个分数的分子和分母的最大公约数是1,这个分数称为既约分数。例如,3/4 , 5/2 , 1/8 , 7/1都是既约分数。请问,有多少个既约分数,分子和分母都是1 到2020 之间的整数(包括1和2020)?

分析:二重循环+判断最大公约数

欧几里算法——辗转相除法:以除数和余数反复做除法运算,当余数为 0 时,取当前算式除数为最大公约数。

假如需要求 1997 和 615 两个正整数的最大公约数,用欧几里得算法,是这样进行的:

1997 / 615 = 3 (余 152)

615 / 152 = 4(余7)

152 / 7 = 21(余5)

7 / 5 = 1 (余2)

5 / 2 = 2 (余1)

2 / 1 = 2 (余0)

至此,最大公约数为1

以除数和余数反复做除法运算,当余数为 0 时,取当前算式除数为最大公约数,所以就得出了 1997 和 615 的最大公约数 1。

public class Main {
    public static void main(String[] args) {
        int cnt = 0;
        for (int i = 1;i <= 2020;i++){
            for (int j = 1;j <= 2020;j++){
                if (gcd(i,j)==1) cnt++;
            }
        }
        System.out.println(cnt);
    }
    public static int gcd(int i,int j){
        if(i % j == 0) return j;//当余数为 0 时,取当前算式除数为最大公约数
        return gcd(j, i % j);//当余数不为 0 时,取除数为下一个式子的被除数,取余数为下一个式子的除数
    }
}

  • 最大公共子串

求两个串的所有子串中能够匹配上的最大长度是多少。
比如:"abcdkkk" 和 "baabcdadabc",
可以找到的最长的公共子串是"abcd",所以最大公共子串长度为4。

 分析:矩阵法

int[][] a = new int[c1.length+1][c2.length+1];//作为判断两个字符串数组是否匹配
        //若对应位置匹配成功,则对应的二维矩阵值变化

 只有当连续匹配成功时,最大匹配字符数才能一直变大,二维矩阵的值即为匹配成功的字符数,最大值为连续匹配的最大数

int max = 0;
        for(int i=1; i<a.length; i++){
            for(int j=1; j<a[i].length; j++){
                if(c1[i-1]==c2[j-1]) {
                    a[i][j] = a[i-1][j-1]+1;
                    if(a[i][j] > max) max = a[i][j];
                }
            }
        }
"abcdkkk"
"baabcdadabc"
A12=A01+1  A13=A02+1 ...
A21=A10+1  A24=A13+1 ...
public class Main
{
    static int f(String s1, String s2)
    {
        char[] c1 = s1.toCharArray();//将字符串转换为字符串数组
        char[] c2 = s2.toCharArray();

        int[][] a = new int[c1.length+1][c2.length+1];//作为判断两个字符串数组是否匹配
        //若对应位置匹配成功,则对应的二维矩阵值变化

        int max = 0;
        for(int i=1; i<a.length; i++){
            for(int j=1; j<a[i].length; j++){
                if(c1[i-1]==c2[j-1]) {
                    a[i][j] = a[i-1][j-1]+1;
                    if(a[i][j] > max) max = a[i][j];
                }
            }
        }

        return max;
    }

    public static void main(String[] args){
        int n = f("abcdkkk", "baabcdadabc");
        System.out.println(n);
        System.out.println(f("aaakkkabababa", "baabababcdadabc"));
        System.out.println(f("abccbaacbcca", "ccccbbbbbaaaa"));
        System.out.println(f("abcd", "xyz"));
        System.out.println(f("ab", "ab"));

    }
}

  • 方格分割

题目描述

6x6的方格,沿着格子的边线剪开成两部分。要求这两部分的形状完全相同。

 

试计算:
包括这3种分法在内,一共有多少种不同的分割方法。
注意:旋转对称的属于同一种分割法。

分析:DFS算法的应用 

从方格的中心出发,当走到一个边的尽头时,将路径对折便形成了一条封闭的路径该路径恰好可以讲方格分成相等的两部分。路径用dfs算法求得。由于可以沿左右或者上下两种方式对折,且对折本身就是对称的,所以这样求得的路径数是题目需要的四倍。最后的答案应除以4。

public class Main {

    public static int ans = 0;//分割方格数
    public static boolean[][] matrix = new boolean[7][7];//标记数组
    public static int[][] method = {{1,0},{-1,0},{0,1},{0,-1}};//前进方式

    public static void main(String[] args) {
        matrix[3][3] = true;//初始化标记,中心位置
        dfs(3,3);
        System.out.println(ans/4);
    }
    private static void dfs(int x, int y){
        //走到尽头,返回
        if(x==0||x==6||y==0||y==6){
            ans++;
            return;
        }
        //用dfs继续走
        else {
            int i;
            int nx;
            int ny;
            for (i = 0;i<4;i++){//四种前进方式
                nx = x + method[i][0];
                ny = y + method[i][1];
                if (!matrix[nx][ny]){
                    matrix[nx][ny] = true;
                    matrix[6-nx][6-ny] = true;
                    dfs(nx,ny);//方格是对称的,因此不需要求重复的
                    matrix[nx][ny] = false;
                    matrix[6-nx][6-ny] = false;
                }
            }

        }
    }
}

  • 承压计算

  • X星球的高科技实验室中整齐地堆放着某批珍贵金属原料。

    每块金属原料的外形、尺寸完全一致,但重量不同。

    金属材料被严格地堆放成金字塔形。

                                  7 
                                5 8 
                               7 8 8 
                              9 2 7 2 
                             8 1 4 9 1 
                            8 1 8 8 4 1 
                           7 9 6 1 4 5 4 
                          5 6 5 5 6 9 5 6 
                         5 5 4 7 9 3 5 5 1 
                        7 5 7 9 7 4 7 3 3 1 
                       4 6 4 5 5 8 8 3 2 4 3 
                      1 1 3 3 1 6 6 5 5 4 4 2 
                     9 9 9 2 1 9 1 9 2 9 5 7 9 
                    4 3 3 7 7 9 3 6 1 3 8 8 3 7 
                   3 6 8 1 5 3 9 5 8 3 8 1 8 3 3 
                  8 3 2 3 3 5 5 8 5 4 2 8 6 7 6 9 
                 8 1 8 1 8 4 6 2 2 1 7 9 4 2 3 3 4 
                2 8 4 2 2 9 9 2 8 3 4 9 6 3 9 4 6 9 
               7 9 7 4 9 7 6 6 2 8 9 4 1 8 1 7 2 1 6 
              9 2 8 6 4 2 7 9 5 4 1 2 5 1 7 3 9 8 3 3 
             5 2 1 6 7 9 3 2 8 9 5 5 6 6 6 2 1 8 7 9 9 
            6 7 1 8 8 7 5 3 6 5 4 7 3 4 6 7 8 1 3 2 7 4 
           2 2 6 3 5 3 4 9 2 4 5 7 6 6 3 2 7 2 4 8 5 5 4 
          7 4 4 5 8 3 3 8 1 8 6 3 2 1 6 2 6 4 6 3 8 2 9 6 
         1 2 4 1 3 3 5 3 4 9 6 3 8 6 5 9 1 5 3 2 6 8 8 5 3 
        2 2 7 9 3 3 2 8 6 9 8 4 4 9 5 8 2 6 3 4 8 4 9 3 8 8 
       7 7 7 9 7 5 2 7 9 2 5 1 9 2 6 5 3 9 3 5 7 3 5 4 2 8 9 
      7 7 6 6 8 7 5 5 8 2 4 7 7 4 7 2 6 9 2 1 8 2 9 8 5 7 3 6 
     5 9 4 5 5 7 5 5 6 3 5 3 9 5 8 9 5 4 1 2 6 1 4 3 5 3 2 4 1 
    X X X X X X X X X X X X X X X X X X X X X X X X X X X X X X 

    其中的数字代表金属块的重量

    最下一层的X代表30台极高精度的电子秤。

    假设每块原料的重量都十分精确地平均落在下方的两个金属块上,

    最后,所有的金属块的重量都严格精确地平分落在最底层的电子秤上。

    电子秤的计量单位很小,所以显示的数字很大。

    工作人员发现,其中读数最小的电子秤的示数为:2086458231

    请你推算出:读数最大的电子秤的示数为多少?

    思路:用两个for循环,将上一层的重量平分到下一层的,最后将最后一层的数据取出minVal,maxVal用2086458231 除以 minVal 得到每一份的大小,再乘以最大值得出结果 72665192664

    import java.util.Scanner;
    public class Main {
    
        public static void main(String[] args) {
    
            Scanner in = new Scanner(System.in);
    
            double[][] a = new double[30][30];
    
            for (int i = 0; i < a.length - 1; i++) {
                for (int j = 0; j <= i; j++) {
                    a[i][j] = in.nextDouble();
                }
            }
            for (int i = 0; i < 29; i++) {
                for (int j = 0; j <= i; j++) {
                    double avg = a[i][j] / 2.0;//平分,分到相邻的两个电子秤
                    a[i + 1][j] += avg;//左秤
                    a[i + 1][j + 1] += avg;//右秤
                }
            }
    
            double minVal = 9999999;
            double maxVal = -9999999;
    
            for (int i = 0; i < 30; i++) {//找出最后一行示数最值
                if (a[29][i] < minVal)
                    minVal = a[29][i];
                if (a[29][i] > maxVal)
                    maxVal = a[29][i];
            }
    
            System.out.println(2086458231.0 / minVal * maxVal);//最终的示数 最小示数/(示数的最大值*示数的最小值)
        }
    }

  • 后缀表达式

给定 N 个加号、M 个减号以及 N + M + 1 个整数 A1, A2, · · · , AN+M+1,小 明想知道在所有由这 N 个加号、M 个减号以及 N + M + 1 个整数凑出的合法的 后缀表达式中,结果最大的是哪一个?
请你输出这个最大的结果。
例如使用1 2 3 + -,则 “2 3 + 1 -” 这个后缀表达式结果是 4,是最大的。
输入格式
第一行包含两个整数 N 和 M。
第二行包含 N + M + 1 个整数 A1, A2, · · · , AN+M+1。
输出格式
输出一个数,表示答案
样例输入
1 1
1 2 3
样例输出
4

 分析:

1.前缀表达式就是把+-*/ 都放在前面 比如 ++1 1 2 = 4

   后缀表达式就是把+-*/ 都放在后面 比如 1 1 2 ++ = 4

2.遇到数字时入栈,遇到+-*/时取出栈中的前两个数做运算结果再入栈,反复如此。

3.后缀表达式时可以顺便加括号的。所以我们只要有一个负号,我们就可以随便更改其他负号,毕竟负负得正,负正得负,我们只需要减掉最小值,加上最大值,再加上中间所有数的绝对值,得到的结果就是答案最大值。

import java.util.Arrays;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n,m;
        long f=0,sum=0;
        n=sc.nextInt();
        m=sc.nextInt();
        long arr[]=new long [n+m+1];
        for (int i = 0; i < n+m+1; i++) {
            arr[i]=sc.nextLong();
            if(arr[i]<0) {//统计负数的个数
                f++;
            }
            sum+=arr[i];//首先先将所有数加一块
        }

        if(m!=0) {//有减号
            Arrays.sort(arr);//从小到大排序,即前f个为负数
            if(f>0) {//存在负数时
                if (f!=n+m+1) {
                    for(int i=0;i<f;i++) {
                        sum=sum-2*arr[i];//每一个都能变成正数加上,故在原值上加上2倍绝对值
                    }
                }else {//如果全是负数把正号留给最大的值,负号留给其余所有负数
                    for(int i=0;i<f-1;i++) {
                        sum=sum-2*arr[i];
                    }
                }
            }else {//不存在负数,把负号留给最小的值
                sum=sum-2*arr[0];
            }
        }

        System.out.println(sum);
        sc.close();
    }

}

  • 包子凑数

小明几乎每天早晨都会在一家包子铺吃早餐。他发现这家包子铺有N种蒸笼,其中第i种蒸笼恰好能放Ai个包子。每种蒸笼都有非常多笼,可以认为是无限笼。
每当有顾客想买X个包子,卖包子的大叔就会迅速选出若干笼包子来,使得这若干笼中恰好一共有X个包子。比如一共有3种蒸笼,分别能放3、4和5个包子。当顾客想买11个包子时,大叔就会选2笼3个的再加1笼5个的(也可能选出1笼3个的再加2笼4个的)。
当然有时包子大叔无论如何也凑不出顾客想买的数量。比如一共有3种蒸笼,分别能放4、5和6个包子。而顾客想买7个包子时,大叔就凑不出来了。
小明想知道一共有多少种数目是包子大叔凑不出来的。 

分析:动态规划问题 

1.由扩展欧几里算法 ax + by + cz = m ; 当m不是gcd(a,b,c)的倍数时,无解。 <"对本题而言:若输入的数不互质,则凑不出的数有无数个">

2.对任何整数 a,b 和他们的最大公约数d,关于未知数 x和 y的 ax + by = m 有整数解时,当且仅当m是d的倍数。

4. 当最大公约数为1时,即互质如果某个数能凑出,那么它加上任意一个输入的包子数,都能凑出,因此从零到10000扫描n次 ,逐个检查是否能凑出

import java.util.Scanner;
public class Main{
      //  计算两个数的最大公约数
    static int gcd1 (int a,int b){
        return b==0?a:gcd1(b,a%b);
    }
      //  计算多个数的最大公约数
    static int gcd2(int [] a){
        int n = a.length;
        int L = a[0];
        for(int i=1;i<n;i++)
            L = gcd1(L,a[i]);
        return L;
     } 
     public static void main(String[] args) {
    	// TODO Auto-generated method stub
         Scanner in = new Scanner(System.in);
   	 int n = in.nextInt();
   	 int [] a = new int[n];
  	 boolean []b = new boolean [10000];
  	 b[0] = true;
  	 for(int i=0;i<n;i++) {
             a[i]=in.nextInt();
   	 }
   	int gcd = gcd2(a);
   	if(gcd!=1) {
	    System.out.println("INF");
	    System.exit(0); 
	}else {  
     	
         for(int i = 0;i<n;i++) {
             for(int j=0;j+a[i]<10000;j++) {
                   if(b[j]) b[j+a[i]] = true;
              }
         }
     }
     int count = 0; 
     for(int i=0;i<10000;i++) {
         if(b[i]==false) count++;
    }
    System.out.println(count);
    }
}

  • 日期问题

小明正在整理一批历史文献。这些历史文献中出现了很多日期。小明知道这些日期都在1960年1月1日至2059年12月31日。令小明头疼的是,这些日期采用的格式非常不统一,有采用年/月/日的,有采用月/日/年的,还有采用日/月/年的。更加麻烦的是,年份也都省略了前两位,使得文献上的一个日期,存在很多可能的日期与其对应。​
比如02/03/04,可能是2002年03月04日、2004年02月03日或2004年03月02日。
给出一个文献上的日期,你能帮助小明判断有哪些可能的日期对其对应吗?
输入
一个日期,格式是"AA/BB/CC"。 (0 <= A, B, C <= 9)
输出
输出若干个不相同的日期,每个日期一行,格式是"yyyy-MM-dd"。多个日期按从早到晚排列。
样例输入
02/03/04
样例输出
2002-03-04
2004-02-03
2004-03-02

 分析:

1、闰年和平年的判断

2、对月日满足条件的判断

3、对日期判重,相同的日期只能输出一个

4、要对结果进行从早到晚排序

5、在输入时如何保证前导0的输出,如2002-03-02,而不是输出2002-3-2. 

用TreeSet存储字符串,以达到去重和按字典序排序的效果

import java.util.*;
public class Main{
    static Set<String> set = new TreeSet<String>();
    public static boolean isLeapYear(int year){//判断闰年
        return year%400==0 || ( year%4==0 && year%100 !=0 );
    }
    public static void check(int a,int b , int c){//注意事项
        if(a>=60 && a<=99)  a += 1900;
        else if(a>=0  && a<=59)  a += 2000;
        else return;
        if(b>12||b<=0) return;
        if(c>31||c<=0) return;

        switch (b) {
            case 4:
            case 6:
            case 9:
            case 11:
                if(c>30) return ;
                break;
            case 2:
                if(isLeapYear(a)) {
                    if(c>29) return;
                }else {
                    if(c>28) return;
                }
        }
        set.add(a+"-"+(b<10?"0"+b:b)+"-"+(c<10?"0"+c:c));
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Scanner in = new Scanner(System.in);
        String s = in.next();
        int a = (s.charAt(0)-'0')*10+s.charAt(1)-'0';
        int b = (s.charAt(3)-'0')*10+s.charAt(4)-'0';
        int c = (s.charAt(6)-'0')*10+s.charAt(7)-'0';
        check(a, b, c);
        check(c, a, b);
        check(c, b, a);
        for(String str:set) {
            System.out.println(str);
        }
    }
}

  • 等差数列

【问题描述】
数学老师给小明出了一道等差数列求和的题目。但是粗心的小明忘记了一
部分的数列,只记得其中 N 个整数。
现在给出这 N 个整数,小明想知道包含这 N 个整数的最短的等差数列有
几项?
【输入格式】
输入的第一行包含一个整数 N。
第二行包含 N 个整数 A 1 ,A 2 ,··· ,A N 。(注意 A 1 ∼ A N 并不一定是按等差数
列中的顺序给出)
【输出格式】
输出一个整数表示答案。
【样例输入】
5
2 6 4 10 20
【样例输出】
10

思路:先排序,之后相邻两个数之间的最小差值即为公差。 

注意:当公差为0时,需要额外考虑


import java.util.Arrays;
import java.util.Scanner;


public class Main {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int num = sc.nextInt();
        long arr[]=new long[num];

        for (int i = 0; i < arr.length; i++) {
            arr[i]=sc.nextLong();
        }
        Arrays.sort(arr);
        //排序完之后就是 一个有序的数组,那么相邻之间数的差的最小值即为公差
        long min = arr[1]-arr[0];
        for (int i = 1; i < arr.length-1; i++) {
            if (min>arr[i+1]-arr[i]) {
                min=arr[i+1]-arr[i];
            }
        }

        if(min!=0){
        //最大值减去最小值除以公差然后+1就是最短的等差数列的元素个数了
        System.out.println((arr[num-1]-arr[0])/min+1);}
        else System.out.println(num);
    }
}


  • 完全二叉树的权值

题目描述
给定一棵包含N 个节点的完全二叉树,树上每个节点都有一个权值,按从
上到下、从左到右的顺序依次是A1, A2, AN,如下图所示:

现在小明要把相同深度的节点的权值加在一起,他想知道哪个深度的节点
权值之和最大?如果有多个深度的权值和同为最大,请你输出其中最小的深度。
注:根的深度是1。
输入
第一行包含一个整数N。
第二行包含N 个整数A1, A2, AN
对于所有评测用例,1<=N<=100000, -100000<=Ai<=100000。
输出
输出一个整数代表答案。
样例输入

7
1 6 5 4 3 2 1

样例输出

2

分析:

  • 完全二叉树除去最后一层,是一个满二叉树,也就是说每一个节点都有左右子节点。第一层1个节点,第二次2个节点,第三次2^2个节点,依次类推
  • 题目是要把每一层的节点的权值相加的和进行比较,输出最大的权值和所在的那一层,要是权值出现相同,输出权值相加层数最少的那一层。
import java.util.Scanner;
public class Main {
    static int n;
    static int a[]=new int[100005];
    public static void main(String[] args) {
        Scanner scan=new Scanner(System.in);
        n=scan.nextInt();
        for(int i=1;i<=n;i++) a[i]=scan.nextInt();
        long max=-1000000009;
        int height=0;
        int k=0;
        for(int i=1;i<=n;i=i+(int)Math.pow(2, k-1)) {
            long sum=0;
            //每一层可能不满,一定要加上j<=n,否则数组越界
            for(int j=i;j<i+(int)Math.pow(2, k)&&j<=n;j++) {
                sum+=a[j];
            }
            if(sum>max) {
                height=k+1;
                max=sum;
            }
            k++;
        }
        System.out.println(height);
    }
}

  • 分巧克力

儿童节那天有K位小朋友到小明家做客。小明拿出了珍藏的巧克力招待小朋友们。
小明一共有N块巧克力,其中第i块是Hi x Wi的方格组成的长方形。

为了公平起见,小明需要从这 N 块巧克力中切出K块巧克力分给小朋友们。切出的巧克力需要满足:

1. 形状是正方形,边长是整数  
2. 大小相同  

例如一块6x5的巧克力可以切出6块2x2的巧克力或者2块3x3的巧克力。

当然小朋友们都希望得到的巧克力尽可能大,你能帮小Hi计算出最大的边长是多少么?

输入
第一行包含两个整数N和K。(1 <= N, K <= 100000)
以下N行每行包含两个整数Hi和Wi。(1 <= Hi, Wi <= 100000)
输入保证每位小朋友至少能获得一块1x1的巧克力。

输出
输出切出的正方形巧克力最大可能的边长。

样例输入:
2 10
6 5
5 6

样例输出:
2

分析:如果单独的使用逐个循环暴力,必超时,所以使用二分法降低复杂度 直到 l=r=mid ,得出最终结果 

import java.util.Scanner;

public class Main{

    public static void main(String args[])
    {

        int low=1;
        int high=100001;
        int ans =0;

        Scanner sc = new Scanner(System.in);
        int[] h =  new int[100000];
        int[] w =  new int[100000];
        int n = sc.nextInt();
        int k =sc.nextInt();
        for(int i=0;i<n;i++){
            h[i]=sc.nextInt();
            w[i]=sc.nextInt();
        }
        while(low<=high)//采用二分法遍历
        {
            int mid =(low+high)/2;
            int cnt=0;
            for(int i=0;i<n;i++)
            {
                cnt+= (h[i]*w[i])/(mid*mid);//必须是正方形
                mid=(low+high)/2;
            }
            if(cnt>=k)
            {
                low=mid+1;
                ans=mid;
            }
            else{
                high=mid-1;
            }

        }
        System.out.println(ans);
    }
}
  • 等差素数列

题目:2,3,5,7,11,13,….是素数序列。
类似:7,37,67,97,127,157 这样完全由素数组成的等差数列,叫等差素数数列。
上边的数列公差为30,长度为6。
2004年,格林与华人陶哲轩合作证明了:存在任意长度的素数等差数列。
这是数论领域一项惊人的成果!
长度为10的等差素数列,其公差最小值是多少?

分析:

暴力破解

两层循环,一层循环用于循环公差,一层循环用于循环起始素数。须要注意的是,内层循环起始素数的时候,不能无边界循环下去,要设置一个上限(可以选择一个相对较大的数)。

public class Main {
    public static boolean isPrime(int n){
        boolean flag = true;
        for(int i=2;i<=Math.sqrt(n);i++){
            if(n%i==0){
                flag = false;
            }
        }
        return flag;
    }
    public static void main(String[] args) {
        for(int i=0;i<10000;i++){
            if(isPrime(i)){
                for(int j=1;j<10000;j++){
                    if(isPrime(i+j) && isPrime(i+2*j) && isPrime(i+3*j) && isPrime(i+4*j) && isPrime(i+5*j) && isPrime(i+6*j) && isPrime(i+7*j) && isPrime(i+8*j) && isPrime(i+9*j)){
                        System.out.println("公差为:"+j);
                        break;
                    }
                }
            }
        }
    }
}

  • 特别数的和

    【问题描述】

    小明对数位中含有 2、0、1、9 的数字很感兴趣(不包括前导 0),在 1 到 40 中这样的数包括 1、2、9、10 至 32、39 和 40,共 28 个,

    他们的和是 574。 请问,在 1 到 n 中,所有这样的数的和是多少?

    【输入格式】
       输入一行包含两个整数 n。

    【输出格式】
       输出一行,包含一个整数,表示满足条件的数的和。

    【样例输入】 40

    【样例输出】 574

分析:主要是是对取余的应用

import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n =sc.nextInt();
        int sum = 0;
        for(int i=0;i<=n;i++){
            sum=sum+pd(i);
        }
        System.out.println(sum);
    }

    public static int pd(int k){
        int m,n;
        n=k;
        while(n>0){
            m = n % 10;
            if(m==0||m==1||m==2||m==9){
                return k;
            }
            n=n/10;
        }
        return 0;
    }
}
  • 迷宫

下图给出了一个迷宫的平面图,其中标记为 1 的为障碍,标记为 0的为可以通行的地方。

010000
000100
001001
110000
迷宫的入口为左上角,出口为右下角,在迷宫中,只能从一个位置走到这
个它的上、下、左、右四个方向之一。
对于上面的迷宫,从入口开始,可以按DRRURRDDDR 的顺序通过迷宫,
一共 10 步。其中 D、U、L、R 分别表示向下、向上、向左、向右走。
对于下面这个更复杂的迷宫(30 行 50 列),请找出一种通过迷宫的方式,
其使用的步数最少,在步数最少的前提下,请找出字典序最小的一个作为答案。
请注意在字典序中D<L<R<U。
01010101001011001001010110010110100100001000101010
00001000100000101010010000100000001001100110100101
01111011010010001000001101001011100011000000010000
01000000001010100011010000101000001010101011001011
00011111000000101000010010100010100000101100000000
11001000110101000010101100011010011010101011110111
00011011010101001001001010000001000101001110000000
10100000101000100110101010111110011000010000111010
00111000001010100001100010000001000101001100001001
11000110100001110010001001010101010101010001101000
00010000100100000101001010101110100010101010000101
11100100101001001000010000010101010100100100010100
00000010000000101011001111010001100000101010100011
10101010011100001000011000010110011110110100001000
10101010100001101010100101000010100000111011101001
10000000101100010000101100101101001011100000000100
10101001000000010100100001000100000100011110101001
00101001010101101001010100011010101101110000110101
11001010000100001100000010100101000001000111000010
00001000110000110101101000000100101001001000011101
10100101000101000000001110110010110101101010100001
00101000010000110101010000100010001001000100010101
10100001000110010001000010101001010101011111010010
00000100101000000110010100101001000001000000000010
11010000001001110111001001000011101001011011101000
00000110100010001000100000001000011101000000110011
10101000101000100010001111100010101001010000001000
10000010100101001010110000000100101010001011101000
00111100001000010000000110111000000001000000001011
10000001100111010111010001000110111010101101111000

分析:

主要思路:

1.广度优先搜索:遍历从起点到终点,每一个点到重点的最短距离,逆向搜索

2.深度优先搜索:根据广度优先搜索的预处理,深度搜索一条最短的路径

要点:

1.dist数组

//先对整个dist数组的值表示该路是否走过,dist数组的x,y轴表示当前的位置坐标
        for (int i = 0; i < 30; i++) {
            for (int j = 0; j < 50; j++) {
                dist[i][j] = -1;
            }
        }

2. 路径更新的条件

for (int i = 0; i < 4; i++) {
                /*
                    i=0 下
                    i=1 左
                    i=2 右
                    i=3 上
                    表示上下左右的距离大小
                */
                int x = node.x + dX[i];//目前的坐标+移动的距离
                int y = node.y + dY[i];
                /*
                x >= 0 && y >= 0   x < n && y < m  表示没有出迷宫边界
                dist[x][y] == -1  两点间还没有走过
                map[x][y] == 0两点间是通路
                */
                if (x >= 0 && y >= 0 && x < n && y < m && dist[x][y] == -1 && map[x][y] == 0) {
                    //路径更新
                    dist[x][y] = dist[node.x][node.y] + 1;
                    //把通的结点 入列
                    queue.add(new Node(x, y));
                }
            }

3.路径最小的判断

while (x != 29 || y != 49) {
            for (int i = 0; i < 4; i++) {
                /*
                    i=0 下
                    i=1 左
                    i=2 右
                    i=3 上
                */
                int nx = x + dX[i];
                int ny = y + dY[i];
                // 满足以下第一个if语句,只能说该点可走,但是不是最佳选择
                if (nx < 30 && nx >= 0 && ny < 50 && ny >= 0 && map[nx][ny] == 0) {
                    // 如果满足第二个if语句,表示此时所选的下一个即将要走的点是离当前点最近的点(因为0是本身不算,只有1最近)
                    // 而且还是按照下、左、右、上这样的顺序来走的,就完全符合题意了。
                    if (m.dist[x][y] == m.dist[nx][ny] + 1) {
                        x = nx;
                        y = ny;
                        Path += path[i];//更新路径
                        break;
                    }
                }
            }

整体代码: 

package 迷宫;
import java.util.LinkedList;
import java.util.Queue;

public class Main {

    //存储整个迷宫
    int[][] map;

    // 方向由两个一维数组表示 ----    下左右上
    int[] dY = {0, -1, 1, 0};//往左走减一,往右走加一
    int[] dX = {1, 0, 0, -1};//往下走加一,往上走减一

    public Main(int[][] map) {
        this.map = map;
        //先对整个dist数组的值表示该路是否走过,dist数组的x,y轴表示当前的位置坐标
        for (int i = 0; i < 30; i++) {
            for (int j = 0; j < 50; j++) {
                dist[i][j] = -1;
            }
        }
    }

    //用于存储我们的最短路径,也是每个结点到达终点的路径长度
    int[][] dist = new int[30][50];

    int n = 30, m = 50;
    public void BFS() {
        // 用一个队列存储当前的位置结点(结点数据结构在下面)
        Queue<Node> queue = new LinkedList<>();
        //(初始化起点)这里我们选择倒着走整个迷宫,从终点开始走到起点
        dist[n - 1][m - 1] = 0;//从最后一个点开始,直到dist[0][0]回到起点
        //首先添加起点的结点
        queue.add(new Node(n - 1, m - 1));
        //开始BFS
        while (queue.size() != 0) {
            //出队列并由node保存该结点
            Node node = queue.poll();
            for (int i = 0; i < 4; i++) {
                /*
                    i=0 下
                    i=1 左
                    i=2 右
                    i=3 上
                    表示上下左右的距离大小
                */
                int x = node.x + dX[i];//目前的坐标+移动的距离
                int y = node.y + dY[i];
                /*
                x >= 0 && y >= 0   x < n && y < m  表示没有出迷宫边界
                dist[x][y] == -1  两点间还没有走过
                map[x][y] == 0两点间是通路
                */
                if (x >= 0 && y >= 0 && x < n && y < m && dist[x][y] == -1 && map[x][y] == 0) {
                    //路径更新
                    dist[x][y] = dist[node.x][node.y] + 1;
                    //把通的结点 入列
                    queue.add(new Node(x, y));
                }
            }
        }
    }
}

class Node {
    int x;
    int y;

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

class Text {
    public static void main(String[] args) {
        // 首先构建一个迷宫
        String maze = "010101010010110010010101100101101001000010001010100000100010000010101001000010000000100110011010010101111011010010001000001101001011100011000000010000010000000010101000110100001010000010101010110010110001111100000010100001001010001010000010110000000011001000110101000010101100011010011010101011110111000110110101010010010010100000010001010011100000001010000010100010011010101011111001100001000011101000111000001010100001100010000001000101001100001001110001101000011100100010010101010101010100011010000001000010010000010100101010111010001010101000010111100100101001001000010000010101010100100100010100000000100000001010110011110100011000001010101000111010101001110000100001100001011001111011010000100010101010100001101010100101000010100000111011101001100000001011000100001011001011010010111000000001001010100100000001010010000100010000010001111010100100101001010101101001010100011010101101110000110101110010100001000011000000101001010000010001110000100000100011000011010110100000010010100100100001110110100101000101000000001110110010110101101010100001001010000100001101010100001000100010010001000101011010000100011001000100001010100101010101111101001000000100101000000110010100101001000001000000000010110100000010011101110010010000111010010110111010000000011010001000100010000000100001110100000011001110101000101000100010001111100010101001010000001000100000101001010010101100000001001010100010111010000011110000100001000000011011100000000100000000101110000001100111010111010001000110111010101101111000";
        int count = 0;
        int[][] map = new int[30][50];
        for (int i = 0; i < 30; i++) {
            for (int j = 0; j < 50; j++) {
                map[i][j] = Integer.parseInt(maze.substring(count, count + 1));//拆分字符串赋值给二维数组
                count++;
            }
        }

        Main m = new Main(map);
        m.BFS();
        //走到[0][0]即为起点,也就是走完了整个迷宫
        //接下来求出路径
        int x = 0, y = 0;
        // D<L<R<U
        String[] path = {"D", "L", "R", "U"};
        String Path = "";

        int[] dY = {0, -1, 1, 0};
        int[] dX = {1, 0, 0, -1};
        // 没有走到终点,就一直循环下去
        while (x != 29 || y != 49) {
            for (int i = 0; i < 4; i++) {
                /*
                    i=0 下
                    i=1 左
                    i=2 右
                    i=3 上
                */
                int nx = x + dX[i];
                int ny = y + dY[i];
                // 满足以下第一个if语句,只能说该点可走,但是不是最佳选择
                if (nx < 30 && nx >= 0 && ny < 50 && ny >= 0 && map[nx][ny] == 0) {
                    // 如果满足第二个if语句,表示此时所选的下一个即将要走的点是离当前点最近的点(因为0是本身不算,只有1最近)
                    // 而且还是按照下、左、右、上这样的顺序来走的,就完全符合题意了。
                    if (m.dist[x][y] == m.dist[nx][ny] + 1) {
                        x = nx;
                        y = ny;
                        Path += path[i];//更新路径
                        break;
                    }
                }
            }
        }

        System.out.println(Path);
    }
}

  • 数列求值

题目描述

本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。

给定数列 1, 1, 1, 3, 5, 9, 17,从第 4 项开始,每项都是前 3项的和。

求第 20190324项的最后 4位数字。

分析:

本道题就是兔子函数

需要取出末尾四位数字那么只需要对10000取余数即可,因为任何大于10000的数字取余之后那么结果都会在0~9999之间所以自然就可以得到末尾的四位数字,不管怎么样取余数那么结果对于某位四位数字是没有任何影响的,在循环的时候对这四个数字进行动态改变,并且在计算之前需要对每个中间结果进行取余,确保当前记录的变量在10000以下

public class Main {
    public static void main(String[] args) {
        int a=1,b=1,c=1;
        int temp = 0;
        for(int i=4;i<=20190324;i++){
            temp=(a+b+c)%10000;
            a=b;
            b=c;
            c=temp;
        }
        System.out.println(temp);
    }
}
  • 数的分解

题目描述

本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。

把 2019分解成 3个各不相同的正整数之和,并且要求每个正整数都不包含数字 2和 4,一共有多少种不同的分解方法?

注意交换 3个整数的顺序被视为同一种方法,例如 1000+1001+18 和1001+1000+18 被视为同一种。

解析:

1.利用三重循环暴力破解,但是需要注意顺序问题,解决顺序问题只需要规定三个加数的相对大小即可,第一重循环从1~n/3  第二重从i+1~n,第三重从j+1~n即可

2.不包含2和4,单独写出一个函数利用取余判断是否含有2与4

public class Main {
    public static void main(String[] args) {
        int cnt = 0;
        for (int i = 1; i <= 2019/3; i++) {
            for (int j = i+1; j < 2019; j++) {
                for (int k = j+1; k < 2019; k++) {
                    if (i + j + k == 2019 && vs(i) && vs(j) && vs(k)) {
                        cnt++;
                    }
                }
            }
        }
        System.out.println(cnt);
    }
    public static boolean vs(int n){
        int k;
        while(n>0){
            k=n%10;
            if(k==2||k==4) return false;
            n=n/10;
        }
        return true;
    }
}

  • 七夕礼物

题目描述

七夕节要到了,小明决定给暗恋已久的女神小花送一份大礼。

已知蓝桥商店有 nn 个玩偶,第 ii 个玩偶能给人带来 a_iai​ 的好感值。

通过打听,小明得知了七夕节当天女神小花的心情值为 tt,于是小明决定,从商店购买任意个玩偶使得这些玩偶能带来的好感值和女神小花的心情值最接近。

请你输出这最小的 |心情值-好感值| 。

输出描述

输出共一行,包含一个整数,表示答案。

输入输出样例

输入:5 11 1 2 3 4 5

输出:1

解析:注意判断绝对值

.先将好感值进行排序,然后利用尺取法暴力遍历

import java.util.Arrays;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        int t = scan.nextInt();
        int [] arr = new int[n];
        for(int i=0;i<n;i++){
            arr[i]=scan.nextInt();
        }
        Arrays.sort(arr); // 对其进行排序
        int min = 9999999;
        int i,j;
        for(i = 0;i<n;i++){
            for(j=1;j<=n;j++){
            if(arr[i]*j-t<0) {
                if(t-arr[i]*j<min) min = t-arr[i]*j;
            }
            else if(arr[i]*j-t<min) min=arr[i]*j-t;}
        }
        System.out.println(min);
        scan.close();
    }
    public static int jue(int arr, int t, int j){
        if(arr*j-t < 0) return t-arr*j;
        else return arr*j-t;
    }
}

  • 组队

作为篮球队教练,你需要从以下名单中选出 1 号位至 5 号位各一名球员,
组成球队的首发阵容。
每位球员担任 1 号位至 5 号位时的评分如下表所示。请你计算首发阵容 1
号位至 5 号位的评分之和最大可能是多少?

分析:暴力破解

每个球位的可能球员编号从1到20,计算评分之和前判断一下5个球位的球员编号是否互不相同。利用五重循环可以解决。

import java.util.Scanner;
public class Main {
	
	public static void main(String[] args) {
		Scanner reader = new Scanner(System.in);
		int athleticGrade[][] = new int[20][5];//存储运动员成绩
		for (int i = 0; i < 20; i++) {
			reader.nextInt();//将编号数据吸收掉
			for (int j = 0; j < 5; j++) {
				athleticGrade[i][j] = reader.nextInt();
			}
		}
		int maxGrade = 0;//最大评分之和
		for (int a = 0; a < 20; a++) {
			for (int b = 0; b < 20; b++) {
				for (int c = 0; c < 20; c++) {
					for (int d = 0; d < 20; d++) {
						for (int e = 0; e < 20; e++) {
							//如果a,b,c,d,e互不相同
							if (a!=b && a!=c && a!=d && a!=e && b!=c &&
								b!=d && b!=e && c!=d && c!=e && d!=e) 
//序号不能相同{
								int temp = athleticGrade[a][0]+athleticGrade[b][1]
											+athleticGrade[c][2]+athleticGrade[d][3]
											+athleticGrade[e][4];
								maxGrade = maxGrade > temp ? maxGrade : temp;
							}
						}
					}
				}
			}
		}
		System.out.println(maxGrade);
	}
}
  • 年号字串

题目描述

本题为填空题,只需要算出结果后,在代码中使用输出语句将所填结果输出即可。

小明用字母 AA 对应数字 11,BB 对应 22,以此类推,用 ZZ 对应 2626。对于 2727 以上的数字,小明用两位或更长位的字符串来对应,例如 AAAA 对应 2727,ABAB 对应 2828,AZAZ 对应 5252,LQLQ 对应 329329。

请问 20192019 对应的字符串是什么?

分析一:递归方法

  • 如果输入的数字是26的倍速那么最后一位一定是Z
  • 如果树的数字是不是的26的倍数,但是复合26的倍数+n(n<26),那么最后一位向前进n-1,从A开始往后加
  • 并且与此倒数第二个字母会向前加1
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int num = sc.nextInt();
        System.out.println(change(num));
    }

    public static String change (int num) {
        if (num == 0) return "";
        int temp = num % 26;
        int nextN = num / 26;
        if (temp == 0 ) {
            return change(nextN - 1) + 'Z';
        }
        else {
            return change(nextN) + (char)('A' + temp - 1);
        }
    }
}

 分析二:正常方法

多重循环暴力破解——适用于填空题

缺点:循环的次数需要根据输入的数字来确定

1-26  一重循环

27-52 二重循环

。。。。

public class Main {
    public static void main(String[] args)  {
        int a=26;
        Scanner sc = new Scanner(System.in);
        int ch = sc.nextInt();
        for(char i='A';i<='Z';i++)
            for(char j='A';j<='Z';j++)
                for(char z='A';z<='Z';z++) {
                    a++;
                    if(a==ch) {
                        System.out.println(i+""+j+""+z);
                        break;
                    }
                }
    }

}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Yoin.

感谢各位打赏!!

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

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

打赏作者

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

抵扣说明:

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

余额充值